@transitive-sdk/utils-web 0.14.8 → 0.14.9
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/client/shared.jsx +140 -3
- package/dist/utils-web.js +110 -31
- package/dist/utils-web.js.map +2 -2
- package/docs/client.md +53 -0
- package/package.json +1 -1
package/client/shared.jsx
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import React, { useState, useEffect, useMemo, useRef } from 'react';
|
|
2
|
-
import { Button, Accordion, AccordionContext, Card, Badge
|
|
3
|
-
from 'react-bootstrap';
|
|
2
|
+
import { Button, Accordion, AccordionContext, Card, Badge, Dropdown,
|
|
3
|
+
ButtonGroup, Form } from 'react-bootstrap';
|
|
4
4
|
import ReactWebComponent from './react-web-component';
|
|
5
5
|
|
|
6
6
|
import { parseCookie, decodeJWT } from './client';
|
|
7
7
|
import { useCapability } from './hooks';
|
|
8
8
|
|
|
9
|
+
const F = React.Fragment;
|
|
10
|
+
|
|
9
11
|
const styles = {
|
|
10
12
|
badge: {
|
|
11
13
|
width: '4em'
|
|
@@ -21,7 +23,25 @@ const styles = {
|
|
|
21
23
|
inlineCode: {
|
|
22
24
|
color: '#700',
|
|
23
25
|
margin: '0px 0.5em 0px 0.5em',
|
|
24
|
-
}
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
selector: {
|
|
29
|
+
marginBottom: '1em',
|
|
30
|
+
width: '100%',
|
|
31
|
+
},
|
|
32
|
+
field: {
|
|
33
|
+
borderBottomLeftRadius: '0px',
|
|
34
|
+
borderTopLeftRadius: '0px',
|
|
35
|
+
marginBottom: '1em',
|
|
36
|
+
flex: '100 1 10000em',
|
|
37
|
+
},
|
|
38
|
+
sourceForm: {
|
|
39
|
+
// display: 'flex',
|
|
40
|
+
// gap: '1rem',
|
|
41
|
+
display: 'inline-block',
|
|
42
|
+
width: '100%',
|
|
43
|
+
},
|
|
44
|
+
|
|
25
45
|
};
|
|
26
46
|
|
|
27
47
|
const levelBadges = [
|
|
@@ -46,6 +66,7 @@ export const InlineCode = ({children}) => <tt style={styles.inlineCode}>
|
|
|
46
66
|
|
|
47
67
|
const intervals = {};
|
|
48
68
|
|
|
69
|
+
/** A Timeout component: removes the children once time runs out */
|
|
49
70
|
export const TimerContext = React.createContext({});
|
|
50
71
|
export const Timer = ({duration, onTimeout, onStart, setOnDisconnect, children}) => {
|
|
51
72
|
duration = duration || 60;
|
|
@@ -368,3 +389,119 @@ export const createWebComponent = (Component, name, version = '0.0.0',
|
|
|
368
389
|
return ReactWebComponent.create(Wrapper, name, options.shadowDOM || false,
|
|
369
390
|
compRef);
|
|
370
391
|
};
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
/** takes options in the below format and renders a "tree of dropdowns" to
|
|
395
|
+
* allow user to select from these options in sequence.
|
|
396
|
+
* Format:
|
|
397
|
+
* ```js
|
|
398
|
+
* { selector: 'video source',
|
|
399
|
+
* field: 'type',
|
|
400
|
+
* options: [
|
|
401
|
+
* { label: 'ROS Topic', // label for this option (in parent selector)
|
|
402
|
+
* value: 'rostopic' // value to use when selected
|
|
403
|
+
* field: 'value', // the field for which options list possible values
|
|
404
|
+
* selector: 'ROS Version', // label for next selector
|
|
405
|
+
* options: [
|
|
406
|
+
* { label: 'ROS1',
|
|
407
|
+
* options: [{
|
|
408
|
+
* label: 'topic1',
|
|
409
|
+
* value: 'topic1'
|
|
410
|
+
* }],
|
|
411
|
+
* }, {
|
|
412
|
+
* label: 'Free form',
|
|
413
|
+
* value: 'free-form',
|
|
414
|
+
* selector: 'Enter text',
|
|
415
|
+
* field: 'textParam',
|
|
416
|
+
* }, {
|
|
417
|
+
* label: 'A Number',
|
|
418
|
+
* value: 'free-form-number',
|
|
419
|
+
* selector: 'Enter number',
|
|
420
|
+
* type: 'number',
|
|
421
|
+
* field: 'numberParam',
|
|
422
|
+
* }, {
|
|
423
|
+
* label: 'A Date',
|
|
424
|
+
* value: 'free-form-date',
|
|
425
|
+
* selector: 'Enter date',
|
|
426
|
+
* type: 'datetime-local',
|
|
427
|
+
* field: 'dateParam',
|
|
428
|
+
* }
|
|
429
|
+
* }
|
|
430
|
+
* },
|
|
431
|
+
* ...
|
|
432
|
+
* ]
|
|
433
|
+
* }
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
export const TreeSelector = (props) => {
|
|
437
|
+
const {selector, field, options} = props.options;
|
|
438
|
+
|
|
439
|
+
const preselectedOption = props.preselected &&
|
|
440
|
+
options.find(o => _.isEqual(o.value, props.preselected[field]));
|
|
441
|
+
|
|
442
|
+
// log.debug(preselectedOption);
|
|
443
|
+
|
|
444
|
+
const [selected, setSelected] = useState(preselectedOption);
|
|
445
|
+
const select = (choice) => {
|
|
446
|
+
setSelected(choice);
|
|
447
|
+
(choice != selected) && props.onSelect?.({
|
|
448
|
+
selected: {[field]: choice.value},
|
|
449
|
+
// Indicate when there are no more options to select from, i.e., the
|
|
450
|
+
// selection is complete:
|
|
451
|
+
complete: !choice.options && !choice.field
|
|
452
|
+
});
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const dropDowns = <F>
|
|
456
|
+
<Dropdown as={ButtonGroup} style={styles.selector}>
|
|
457
|
+
<Dropdown.Toggle variant="outline-secondary">
|
|
458
|
+
{selected?.label || selector}
|
|
459
|
+
</Dropdown.Toggle>
|
|
460
|
+
<Dropdown.Menu variant="dark">
|
|
461
|
+
{options.map((option, i) =>
|
|
462
|
+
<Dropdown.Item key={i}
|
|
463
|
+
disabled={option.disabled}
|
|
464
|
+
onClick={() => select(option)}>
|
|
465
|
+
{option.label}
|
|
466
|
+
</Dropdown.Item>
|
|
467
|
+
)}
|
|
468
|
+
</Dropdown.Menu>
|
|
469
|
+
</Dropdown>
|
|
470
|
+
|
|
471
|
+
{selected?.options ?
|
|
472
|
+
<TreeSelector key={JSON.stringify(selected.value)}
|
|
473
|
+
nested={true}
|
|
474
|
+
options={selected}
|
|
475
|
+
preselected={props.preselected} // TODO: is this right?
|
|
476
|
+
onSelect={(subChoice) => {
|
|
477
|
+
const merged = {
|
|
478
|
+
selected: {[field]: selected.value, ...subChoice.selected},
|
|
479
|
+
complete: subChoice.complete
|
|
480
|
+
};
|
|
481
|
+
props.onSelect?.(merged);
|
|
482
|
+
}}
|
|
483
|
+
/>
|
|
484
|
+
: // no options given, just provide an input field
|
|
485
|
+
selected?.field && <Form.Control style={styles.field}
|
|
486
|
+
type={selected.type || 'text'}
|
|
487
|
+
placeholder={selected.selector}
|
|
488
|
+
defaultValue={props.preselected?.[selected.field]}
|
|
489
|
+
onBlur={e => {
|
|
490
|
+
props.onSelect?.({
|
|
491
|
+
selected: {
|
|
492
|
+
[field]: selected.value,
|
|
493
|
+
[selected.field]: e.target.value
|
|
494
|
+
},
|
|
495
|
+
complete: e.target.value.length > 0
|
|
496
|
+
});
|
|
497
|
+
}}
|
|
498
|
+
/>
|
|
499
|
+
}
|
|
500
|
+
</F>;
|
|
501
|
+
|
|
502
|
+
return props.nested ? dropDowns : <div style={styles.sourceForm}>
|
|
503
|
+
<ButtonGroup>
|
|
504
|
+
{dropDowns}
|
|
505
|
+
</ButtonGroup>
|
|
506
|
+
</div>;
|
|
507
|
+
};
|
package/dist/utils-web.js
CHANGED
|
@@ -146,7 +146,7 @@ var require_react_web_component = __commonJS({
|
|
|
146
146
|
// ../common/datacache/tools.js
|
|
147
147
|
var require_tools = __commonJS({
|
|
148
148
|
"../common/datacache/tools.js"(exports, module2) {
|
|
149
|
-
var
|
|
149
|
+
var _3 = {
|
|
150
150
|
get: require("lodash/get"),
|
|
151
151
|
set: require("lodash/set"),
|
|
152
152
|
forEach: require("lodash/forEach"),
|
|
@@ -164,9 +164,9 @@ var require_tools = __commonJS({
|
|
|
164
164
|
return `/${pathArray.map(dropWildcardIds).map(encodeTopicElement).join("/")}`;
|
|
165
165
|
};
|
|
166
166
|
var toFlatObject = (obj, prefix = [], rtv = {}) => {
|
|
167
|
-
|
|
167
|
+
_3.forEach(obj, (value, key) => {
|
|
168
168
|
const newPrefix = prefix.concat(String(key));
|
|
169
|
-
if ((
|
|
169
|
+
if ((_3.isPlainObject(value) || value instanceof Array) && value !== null) {
|
|
170
170
|
toFlatObject(value, newPrefix, rtv);
|
|
171
171
|
} else {
|
|
172
172
|
rtv[pathToTopic2(newPrefix)] = value;
|
|
@@ -280,7 +280,7 @@ var require_common = __commonJS({
|
|
|
280
280
|
"../common/common.js"(exports, module2) {
|
|
281
281
|
var semverCompare = require("semver/functions/compare");
|
|
282
282
|
var semverMinVersion = require("semver/ranges/min-version");
|
|
283
|
-
var
|
|
283
|
+
var _3 = {
|
|
284
284
|
get: require("lodash/get"),
|
|
285
285
|
set: require("lodash/set"),
|
|
286
286
|
unset: require("lodash/unset"),
|
|
@@ -424,16 +424,16 @@ var require_common = __commonJS({
|
|
|
424
424
|
var versionCompare = (a, b) => semverCompare(semverMinVersion(a), semverMinVersion(b));
|
|
425
425
|
var mergeVersions2 = (versionsObject, subTopic = void 0, options = {}) => {
|
|
426
426
|
if (!versionsObject) {
|
|
427
|
-
return subTopic ?
|
|
427
|
+
return subTopic ? _3.set({}, subTopic, versionsObject) : versionsObject;
|
|
428
428
|
}
|
|
429
429
|
const versions = Object.keys(versionsObject).filter((ver) => (!options.maxVersion || versionCompare(ver, options.maxVersion) <= 0) && (!options.minVersion || versionCompare(options.minVersion, ver) <= 0)).sort(versionCompare);
|
|
430
430
|
const merged = {};
|
|
431
431
|
const subPath = subTopic && topicToPath2(subTopic);
|
|
432
432
|
versions.forEach((nextVersion) => {
|
|
433
|
-
const newValue = subPath ?
|
|
434
|
-
|
|
433
|
+
const newValue = subPath ? _3.get(versionsObject[nextVersion], subPath) : versionsObject[nextVersion];
|
|
434
|
+
_3.merge(merged, newValue);
|
|
435
435
|
});
|
|
436
|
-
return subPath ?
|
|
436
|
+
return subPath ? _3.set({}, subPath, merged) : merged;
|
|
437
437
|
};
|
|
438
438
|
var units = ["B", "KB", "MB", "GB", "TB"];
|
|
439
439
|
var formatBytes = (bytes) => {
|
|
@@ -502,7 +502,7 @@ var require_common = __commonJS({
|
|
|
502
502
|
// ../common/datacache/DataCache.js
|
|
503
503
|
var require_DataCache = __commonJS({
|
|
504
504
|
"../common/datacache/DataCache.js"(exports, module2) {
|
|
505
|
-
var
|
|
505
|
+
var _3 = {
|
|
506
506
|
get: require("lodash/get"),
|
|
507
507
|
set: require("lodash/set"),
|
|
508
508
|
unset: require("lodash/unset"),
|
|
@@ -517,22 +517,22 @@ var require_DataCache = __commonJS({
|
|
|
517
517
|
var unset = (obj, path) => {
|
|
518
518
|
if (!path || path.length == 0)
|
|
519
519
|
return;
|
|
520
|
-
|
|
520
|
+
_3.unset(obj, path);
|
|
521
521
|
const parentPath = path.slice(0, -1);
|
|
522
|
-
const parent = parentPath.length == 0 ? obj :
|
|
523
|
-
if (
|
|
522
|
+
const parent = parentPath.length == 0 ? obj : _3.get(obj, parentPath);
|
|
523
|
+
if (_3.isEmpty(parent)) {
|
|
524
524
|
return unset(obj, parentPath);
|
|
525
525
|
} else {
|
|
526
526
|
return path;
|
|
527
527
|
}
|
|
528
528
|
};
|
|
529
529
|
var updateObject = (obj, modifier) => {
|
|
530
|
-
|
|
530
|
+
_3.forEach(modifier, (value, topic) => {
|
|
531
531
|
const path = topicToPath2(topic);
|
|
532
532
|
if (value == null) {
|
|
533
533
|
unset(obj, path);
|
|
534
534
|
} else {
|
|
535
|
-
|
|
535
|
+
_3.set(obj, path, value);
|
|
536
536
|
}
|
|
537
537
|
});
|
|
538
538
|
return obj;
|
|
@@ -565,7 +565,7 @@ var require_DataCache = __commonJS({
|
|
|
565
565
|
to ignore updates with a certain tag.
|
|
566
566
|
*/
|
|
567
567
|
updateFromArray(path, value, tags = {}) {
|
|
568
|
-
const current =
|
|
568
|
+
const current = _3.get(this.#data, path);
|
|
569
569
|
if (value == null) {
|
|
570
570
|
if (current === void 0 || current === null) {
|
|
571
571
|
return {};
|
|
@@ -573,10 +573,10 @@ var require_DataCache = __commonJS({
|
|
|
573
573
|
unset(this.#data, path);
|
|
574
574
|
}
|
|
575
575
|
} else {
|
|
576
|
-
if (
|
|
576
|
+
if (_3.eq(current, value)) {
|
|
577
577
|
return {};
|
|
578
578
|
}
|
|
579
|
-
|
|
579
|
+
_3.set(this.#data, path, value);
|
|
580
580
|
}
|
|
581
581
|
const topic = pathToTopic2(path);
|
|
582
582
|
const obj = { [topic]: value };
|
|
@@ -584,7 +584,7 @@ var require_DataCache = __commonJS({
|
|
|
584
584
|
if (value instanceof Object) {
|
|
585
585
|
const flatValue = toFlatObject(value);
|
|
586
586
|
flatChanges = {};
|
|
587
|
-
|
|
587
|
+
_3.forEach(flatValue, (atomic, flatKey) => {
|
|
588
588
|
flatChanges[`${topic}${flatKey}`] = atomic;
|
|
589
589
|
});
|
|
590
590
|
} else {
|
|
@@ -611,7 +611,7 @@ var require_DataCache = __commonJS({
|
|
|
611
611
|
/** Update data from a modifier object where keys are topic names to be
|
|
612
612
|
interpreted as paths, and values are the values to set */
|
|
613
613
|
updateFromModifier(modifier, tags) {
|
|
614
|
-
return
|
|
614
|
+
return _3.map(modifier, (value, topic) => this.updateFromTopic(topic, value, tags));
|
|
615
615
|
}
|
|
616
616
|
/** Add a callback for all change events. */
|
|
617
617
|
subscribe(callback) {
|
|
@@ -625,7 +625,7 @@ var require_DataCache = __commonJS({
|
|
|
625
625
|
`value, key, matched, tags`. */
|
|
626
626
|
subscribePath(path, callback) {
|
|
627
627
|
this.#listeners.push((changes, tags) => {
|
|
628
|
-
|
|
628
|
+
_3.forEach(changes, (value, key) => {
|
|
629
629
|
const matched = topicMatch(path, key);
|
|
630
630
|
matched && callback(value, key, matched, tags);
|
|
631
631
|
});
|
|
@@ -639,7 +639,7 @@ var require_DataCache = __commonJS({
|
|
|
639
639
|
/** Same as subscribePath but always get all changes in flat form */
|
|
640
640
|
subscribePathFlat(topic, callback) {
|
|
641
641
|
this.#flatListeners.push((changes, tags) => {
|
|
642
|
-
|
|
642
|
+
_3.forEach(changes, (value, key) => {
|
|
643
643
|
const matched = topicMatch(topic, key);
|
|
644
644
|
matched && callback(value, key, matched, tags);
|
|
645
645
|
});
|
|
@@ -651,7 +651,7 @@ var require_DataCache = __commonJS({
|
|
|
651
651
|
}
|
|
652
652
|
/** Get sub-value at path, or entire object if none given */
|
|
653
653
|
get(path = []) {
|
|
654
|
-
return path.length == 0 ? this.#data :
|
|
654
|
+
return path.length == 0 ? this.#data : _3.get(this.#data, path);
|
|
655
655
|
}
|
|
656
656
|
/** Get sub-value specified by topic */
|
|
657
657
|
getByTopic(topic) {
|
|
@@ -699,7 +699,7 @@ var require_datacache = __commonJS({
|
|
|
699
699
|
var require_MqttSync = __commonJS({
|
|
700
700
|
"../common/MqttSync.js"(exports, module2) {
|
|
701
701
|
"use strict";
|
|
702
|
-
var
|
|
702
|
+
var _3 = require("lodash");
|
|
703
703
|
var {
|
|
704
704
|
mqttParsePayload,
|
|
705
705
|
topicMatch,
|
|
@@ -833,7 +833,7 @@ var require_MqttSync = __commonJS({
|
|
|
833
833
|
publishAtLevel(topic, value, level) {
|
|
834
834
|
log2.debug(`publishingAtLevel ${level}`, topic, value);
|
|
835
835
|
if (level > 0) {
|
|
836
|
-
|
|
836
|
+
_3.forEach(value, (subValue, subKey) => {
|
|
837
837
|
const subTopic = `${topic}/${encodeTopicElement(subKey)}`;
|
|
838
838
|
log2.debug(`publishing ${subTopic}`);
|
|
839
839
|
this.publishAtLevel(subTopic, subValue, level - 1);
|
|
@@ -889,7 +889,7 @@ var require_MqttSync = __commonJS({
|
|
|
889
889
|
}
|
|
890
890
|
Object.assign(all, value);
|
|
891
891
|
const merged = mergeVersions2(value, suffix, { maxVersion: newVersion });
|
|
892
|
-
const suffixMergedValue =
|
|
892
|
+
const suffixMergedValue = _3.get(merged, topicToPath2(suffix));
|
|
893
893
|
log2.debug({ value, suffix, merged, suffixMergedValue });
|
|
894
894
|
const transformed = transform ? transform(suffixMergedValue) : suffixMergedValue;
|
|
895
895
|
const newTopic = resolveDoubleSlashes(`${groundPrefix}/${newVersion}/${suffix}`);
|
|
@@ -897,7 +897,7 @@ var require_MqttSync = __commonJS({
|
|
|
897
897
|
if (flat) {
|
|
898
898
|
const flatObj = toFlatObject(transformed);
|
|
899
899
|
const newPath = topicToPath2(newTopic);
|
|
900
|
-
|
|
900
|
+
_3.forEach(flatObj, (value2, key) => {
|
|
901
901
|
const keyTopic = pathToTopic2(newPath.concat(topicToPath2(key)));
|
|
902
902
|
this.mqtt.publish(
|
|
903
903
|
keyTopic,
|
|
@@ -1073,7 +1073,7 @@ var require_MqttSync = __commonJS({
|
|
|
1073
1073
|
of publish queue.
|
|
1074
1074
|
*/
|
|
1075
1075
|
setThrottle(delay) {
|
|
1076
|
-
this._processQueueThrottled =
|
|
1076
|
+
this._processQueueThrottled = _3.throttle(this._processQueue.bind(this), delay);
|
|
1077
1077
|
}
|
|
1078
1078
|
/** Clear the set throttling delay. */
|
|
1079
1079
|
clearThrottle() {
|
|
@@ -1108,7 +1108,7 @@ var require_MqttSync = __commonJS({
|
|
|
1108
1108
|
*/
|
|
1109
1109
|
publish(topic, options = { atomic: false }) {
|
|
1110
1110
|
topic = ensureHashSuffix(topic);
|
|
1111
|
-
if (
|
|
1111
|
+
if (_3.isEqual(this.publishedPaths[topic], options)) {
|
|
1112
1112
|
return false;
|
|
1113
1113
|
}
|
|
1114
1114
|
this.publishedPaths[topic] = options;
|
|
@@ -1134,7 +1134,7 @@ var require_MqttSync = __commonJS({
|
|
|
1134
1134
|
log2.debug("processing change", key);
|
|
1135
1135
|
const path = topicToPath2(key);
|
|
1136
1136
|
const publishedSub = this.publishedMessages.get(path);
|
|
1137
|
-
|
|
1137
|
+
_3.each(publishedSub, (oldSubVal, oldSubKey) => {
|
|
1138
1138
|
if (oldSubKey == specialKey)
|
|
1139
1139
|
return true;
|
|
1140
1140
|
const toClear = Object.keys(toFlatObject(oldSubVal)).filter((subkey) => subkey.endsWith(specialKey));
|
|
@@ -1148,12 +1148,12 @@ var require_MqttSync = __commonJS({
|
|
|
1148
1148
|
const published = this.publishedMessages.get();
|
|
1149
1149
|
visitAncestor(published, path.slice(0, -1), (subObj, prefix) => {
|
|
1150
1150
|
const oldVal = subObj[specialKey];
|
|
1151
|
-
if (oldVal &&
|
|
1151
|
+
if (oldVal && _3.isObject(oldVal)) {
|
|
1152
1152
|
log2.debug("atomic->flat", { oldVal });
|
|
1153
1153
|
const prefixTopic = pathToTopic2(prefix);
|
|
1154
1154
|
this._enqueue(prefixTopic, null);
|
|
1155
1155
|
const flat = toFlatObject(oldVal);
|
|
1156
|
-
|
|
1156
|
+
_3.each(flat, (flatValue, flatKey) => {
|
|
1157
1157
|
const oldFlatKey = `${prefixTopic}${flatKey}`;
|
|
1158
1158
|
this._enqueue(oldFlatKey, flatValue);
|
|
1159
1159
|
});
|
|
@@ -1291,6 +1291,7 @@ __export(web_exports, {
|
|
|
1291
1291
|
Timer: () => Timer,
|
|
1292
1292
|
TimerContext: () => TimerContext,
|
|
1293
1293
|
TransitiveCapability: () => TransitiveCapability,
|
|
1294
|
+
TreeSelector: () => TreeSelector,
|
|
1294
1295
|
createWebComponent: () => createWebComponent,
|
|
1295
1296
|
fetchJson: () => fetchJson,
|
|
1296
1297
|
parseCookie: () => parseCookie,
|
|
@@ -1556,6 +1557,7 @@ var useCapability = ({
|
|
|
1556
1557
|
};
|
|
1557
1558
|
|
|
1558
1559
|
// client/shared.jsx
|
|
1560
|
+
var F = import_react2.default.Fragment;
|
|
1559
1561
|
var styles = {
|
|
1560
1562
|
badge: {
|
|
1561
1563
|
width: "4em"
|
|
@@ -1571,6 +1573,22 @@ var styles = {
|
|
|
1571
1573
|
inlineCode: {
|
|
1572
1574
|
color: "#700",
|
|
1573
1575
|
margin: "0px 0.5em 0px 0.5em"
|
|
1576
|
+
},
|
|
1577
|
+
selector: {
|
|
1578
|
+
marginBottom: "1em",
|
|
1579
|
+
width: "100%"
|
|
1580
|
+
},
|
|
1581
|
+
field: {
|
|
1582
|
+
borderBottomLeftRadius: "0px",
|
|
1583
|
+
borderTopLeftRadius: "0px",
|
|
1584
|
+
marginBottom: "1em",
|
|
1585
|
+
flex: "100 1 10000em"
|
|
1586
|
+
},
|
|
1587
|
+
sourceForm: {
|
|
1588
|
+
// display: 'flex',
|
|
1589
|
+
// gap: '1rem',
|
|
1590
|
+
display: "inline-block",
|
|
1591
|
+
width: "100%"
|
|
1574
1592
|
}
|
|
1575
1593
|
};
|
|
1576
1594
|
var levelBadges = [
|
|
@@ -1780,6 +1798,67 @@ var createWebComponent = (Component, name, version = "0.0.0", options = {}) => {
|
|
|
1780
1798
|
compRef
|
|
1781
1799
|
);
|
|
1782
1800
|
};
|
|
1801
|
+
var TreeSelector = (props) => {
|
|
1802
|
+
const { selector, field, options } = props.options;
|
|
1803
|
+
const preselectedOption = props.preselected && options.find((o) => _.isEqual(o.value, props.preselected[field]));
|
|
1804
|
+
const [selected, setSelected] = (0, import_react2.useState)(preselectedOption);
|
|
1805
|
+
const select = (choice) => {
|
|
1806
|
+
setSelected(choice);
|
|
1807
|
+
choice != selected && props.onSelect?.({
|
|
1808
|
+
selected: { [field]: choice.value },
|
|
1809
|
+
// Indicate when there are no more options to select from, i.e., the
|
|
1810
|
+
// selection is complete:
|
|
1811
|
+
complete: !choice.options && !choice.field
|
|
1812
|
+
});
|
|
1813
|
+
};
|
|
1814
|
+
const dropDowns = /* @__PURE__ */ import_react2.default.createElement(F, null, /* @__PURE__ */ import_react2.default.createElement(import_react_bootstrap.Dropdown, { as: import_react_bootstrap.ButtonGroup, style: styles.selector }, /* @__PURE__ */ import_react2.default.createElement(import_react_bootstrap.Dropdown.Toggle, { variant: "outline-secondary" }, selected?.label || selector), /* @__PURE__ */ import_react2.default.createElement(import_react_bootstrap.Dropdown.Menu, { variant: "dark" }, options.map(
|
|
1815
|
+
(option, i) => /* @__PURE__ */ import_react2.default.createElement(
|
|
1816
|
+
import_react_bootstrap.Dropdown.Item,
|
|
1817
|
+
{
|
|
1818
|
+
key: i,
|
|
1819
|
+
disabled: option.disabled,
|
|
1820
|
+
onClick: () => select(option)
|
|
1821
|
+
},
|
|
1822
|
+
option.label
|
|
1823
|
+
)
|
|
1824
|
+
))), selected?.options ? /* @__PURE__ */ import_react2.default.createElement(
|
|
1825
|
+
TreeSelector,
|
|
1826
|
+
{
|
|
1827
|
+
key: JSON.stringify(selected.value),
|
|
1828
|
+
nested: true,
|
|
1829
|
+
options: selected,
|
|
1830
|
+
preselected: props.preselected,
|
|
1831
|
+
onSelect: (subChoice) => {
|
|
1832
|
+
const merged = {
|
|
1833
|
+
selected: { [field]: selected.value, ...subChoice.selected },
|
|
1834
|
+
complete: subChoice.complete
|
|
1835
|
+
};
|
|
1836
|
+
props.onSelect?.(merged);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
) : (
|
|
1840
|
+
// no options given, just provide an input field
|
|
1841
|
+
selected?.field && /* @__PURE__ */ import_react2.default.createElement(
|
|
1842
|
+
import_react_bootstrap.Form.Control,
|
|
1843
|
+
{
|
|
1844
|
+
style: styles.field,
|
|
1845
|
+
type: selected.type || "text",
|
|
1846
|
+
placeholder: selected.selector,
|
|
1847
|
+
defaultValue: props.preselected?.[selected.field],
|
|
1848
|
+
onBlur: (e) => {
|
|
1849
|
+
props.onSelect?.({
|
|
1850
|
+
selected: {
|
|
1851
|
+
[field]: selected.value,
|
|
1852
|
+
[selected.field]: e.target.value
|
|
1853
|
+
},
|
|
1854
|
+
complete: e.target.value.length > 0
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
)
|
|
1859
|
+
));
|
|
1860
|
+
return props.nested ? dropDowns : /* @__PURE__ */ import_react2.default.createElement("div", { style: styles.sourceForm }, /* @__PURE__ */ import_react2.default.createElement(import_react_bootstrap.ButtonGroup, null, dropDowns));
|
|
1861
|
+
};
|
|
1783
1862
|
|
|
1784
1863
|
// index.js
|
|
1785
1864
|
__reExport(web_exports, client_exports, module.exports);
|
package/dist/utils-web.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../client/react-web-component/getStyleElementsFromReactWebComponentStyleLoader.js", "../client/react-web-component/extractAttributes.js", "../client/react-web-component/index.js", "../../common/datacache/tools.js", "../../common/constants.js", "../../common/common.js", "../../common/datacache/DataCache.js", "../../common/datacache/index.js", "../../common/MqttSync.js", "../index.js", "../client/shared.jsx", "../client/client.js", "../client/hooks.jsx"],
|
|
4
|
-
"sourcesContent": ["/*\n * An optional library which is conditionally added\n * @returns {[]}\n */\nmodule.exports = () => {\n try {\n return require('react-web-component-style-loader/exports').styleElements;\n } catch (e) {\n return [];\n }\n};\n", "/*\n * Takes in a node attributes map and returns an object with camelCased\n * properties and values\n * @param nodeMap\n * @returns {{}}\n */\nmodule.exports = function extractAttributes(nodeMap) {\n if (!nodeMap.attributes) {\n return {};\n }\n\n let obj = {};\n let attribute;\n const attributesAsNodeMap = [...nodeMap.attributes];\n const attributes = attributesAsNodeMap.map((attribute) =>\n ({ [attribute.name]: attribute.value }));\n\n for (attribute of attributes) {\n const key = Object.keys(attribute)[0];\n const camelCasedKey = key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());\n obj[camelCasedKey] = attribute[key];\n }\n\n return obj;\n};\n", "const React = require('react');\nconst ReactDOM = require('react-dom');\nconst { createRoot } = require('react-dom/client');\n\n// const { createRoot } = require('react-dom/client'); // react 18; wip\nconst retargetEvents = require('react-shadow-dom-retarget-events');\nconst getStyleElementsFromReactWebComponentStyleLoader = require('./getStyleElementsFromReactWebComponentStyleLoader');\nconst extractAttributes = require('./extractAttributes');\n\n// require('@webcomponents/shadydom');\n// require('@webcomponents/custom-elements');\n\nconst lifeCycleHooks = {\n attachedCallback: 'webComponentAttached',\n connectedCallback: 'webComponentConnected',\n disconnectedCallback: 'webComponentDisconnected',\n adoptedCallback: 'webComponentAdopted'\n};\n\nmodule.exports = {\n /*\n * @param {JSX.Element} wrapper: the wrapper component class to be instantiated and wrapped\n * @param {string} tagName - The name of the web component. Has to be minus \"-\" delimited.\n * @param {boolean} useShadowDom - If the value is set to \"true\" the web component will use the `shadowDom`. The default value is true.\n */\n create: (wrapper, tagName, useShadowDom = true, compRef = undefined) => {\n\n const proto = class extends HTMLElement {\n instance = null; // the instance we create of the wrapper\n\n callConstructorHook() {\n if (this.instance['webComponentConstructed']) {\n this.instance['webComponentConstructed'].apply(this.instance, [this])\n }\n }\n\n callLifeCycleHook(hook, params = []) {\n const method = lifeCycleHooks[hook];\n if (method && this.instance && this.instance[method]) {\n this.instance[method].apply(this.instance, params);\n }\n }\n\n connectedCallback() {\n const self = this;\n let mountPoint = self;\n\n if (useShadowDom) {\n // Re-assign the self (this) to the newly created shadowRoot\n const shadowRoot = self.attachShadow({ mode: 'open' });\n\n // Re-assign the mountPoint to the newly created \"div\" element\n mountPoint = document.createElement('div');\n\n // Move all of the styles assigned to the react component inside of\n // the shadowRoot. By default this is not used, only if the library is\n // explicitly installed\n const styles = getStyleElementsFromReactWebComponentStyleLoader();\n styles.forEach((style) => {\n shadowRoot.appendChild(style.cloneNode(shadowRoot));\n });\n\n shadowRoot.appendChild(mountPoint);\n retargetEvents(shadowRoot);\n }\n\n createRoot(mountPoint).render(\n // This is where we instantiate the actual component (in its wrapper)\n React.createElement(wrapper, {_element: self, ...extractAttributes(self)})\n );\n }\n\n disconnectedCallback() {\n this.callLifeCycleHook('disconnectedCallback');\n }\n\n adoptedCallback(oldDocument, newDocument) {\n this.callLifeCycleHook('adoptedCallback', [oldDocument, newDocument]);\n }\n\n /* call a function defined in the component, either as a class method, or\n * via useImperativeHandle */\n call(functionName, args) {\n return compRef?.current?.[functionName]?.call(compRef?.current, args);\n }\n\n /* predefined function to retrieve the pre-defined config object of the\n * state, populated via the pre-defined `setConfig` method given as prop\n * to the wrapped component. */\n getConfig() {\n return this.instance.state.config;\n }\n }\n\n customElements.define(tagName, proto);\n\n return proto;\n },\n};\n", "\nconst _ = {\n get: require('lodash/get'),\n set: require('lodash/set'),\n forEach: require('lodash/forEach'),\n map: require('lodash/map'),\n isPlainObject: require('lodash/isPlainObject'),\n};\n\n\n// -------------------------------------------------------------------------\n// DataCache tools\n\n/** convert topic to path array */\nconst topicToPath = (topic) => {\n // split topic by slashes, but not if they are escaped\n // const path = topic.match(/(\\\\.|[^/])+/g) || [];\n // split topic by slashes and unescape slashes in each item\n const path = topic.split('/').map(decodeTopicElement);\n // handle leading slash\n path.length > 0 && path[0].length == 0 && path.shift();\n // handle trailing slash\n path.length > 0 && path.at(-1).length == 0 && path.pop();\n return path;\n};\n\n\n/** convert a path array to mqtt topic; reduces +id identifiers to + */\nconst pathToTopic = (pathArray) => {\n /** reduce wildcards with Ids, such as `+sessionId`, to just + */\n const dropWildcardIds = (x) => x.startsWith('+') ? '+' : x;\n return `/${pathArray.map(dropWildcardIds).map(encodeTopicElement).join('/')}`;\n};\n\n/**\n * Given an object, return a new flat object of topic+value pairs, e.g.:\n```js\n{a: {b: 1, c: 2}, d: 3} \u2192 {'/a/b': 1, '/a/c': 2, '/d': 3}\n```\nNote: not idempotent!\n```js\n{'/a/b': 1, '/a/c': 2, d: 3} \u2192 {'%2Fa%2Fb': 1, '%2Fa%2Fc': 2, '/d': 3}\n```\n*/\nconst toFlatObject = (obj, prefix = [], rtv = {}) => {\n _.forEach(obj, (value, key) => {\n // const newPrefix = prefix.concat(topicToPath(String(key)));\n const newPrefix = prefix.concat(String(key));\n\n // TODO: using isPlainObject also means custom objects (classes) do not get\n // broken down.\n if ((_.isPlainObject(value) || value instanceof Array) && value !== null) {\n // it's an object or array\n toFlatObject(value, newPrefix, rtv);\n } else {\n // it's a primitive\n rtv[pathToTopic(newPrefix)] = value;\n }\n });\n return rtv;\n};\n\n/** Iterate through the object and invoke callback for each match of path (with\nnamed wildcards) */\nconst forMatchIterator = (obj, path, callback, pathSoFar = [], matchSoFar = {}) => {\n\n if (path.length == 0 || path[0] == '#') {\n callback(obj, pathSoFar, matchSoFar);\n return;\n }\n\n const next = path[0]; // don't use shift, we don't want to modify path\n if (next) {\n for (let key in obj) {\n if (key == next || next == '*' || next.startsWith('+')) {\n const match = next.startsWith('+') && next.length > 1 ?\n Object.assign({}, matchSoFar, {[next.slice(1)]: key}) :\n matchSoFar;\n forMatchIterator(obj[key], path.slice(1), callback,\n pathSoFar.concat([key]), match);\n }\n }\n }\n};\n\n/** Like _.set but without arrays. This allows using numbers as keys. */\nconst setFromPath = (obj, path, value) => {\n if (path.length == 0) return obj;\n const next = path.shift();\n if (path.length == 0) {\n obj[next] = value;\n } else {\n if (!obj[next]) obj[next] = {};\n setFromPath(obj[next], path, value);\n }\n};\n\nconst encodeTopicElement = x => x.replace(/%/g, '%25').replace(/\\//g, '%2F');\nconst decodeTopicElement = x => x.replace(/%25/g, '%').replace(/%2F/g, '/');\n\n\n/** Match a slash-separated topic or path array with a selector using +XYZ for\n* (named) wildcards. Return the matching result.\n*/\nconst topicMatch = (selector, topic) => {\n const byArray = (s, p) => {\n if (s.length == 0) return true; // we are done: prefix matched\n if (s[0][0] == '#') return true; // explicit tail-wildcard\n // if (p.length < s.length) return false;\n if (p.length == 0) return true; // we are done, matched all\n // simple match:\n if (s[0] == p[0]) return byArray(s.slice(1), p.slice(1));\n // wild card match:\n if (s[0][0] == '+') {\n const sub = byArray(s.slice(1), p.slice(1));\n return sub && Object.assign({[s[0].slice(1)]: p[0]}, sub);\n }\n // else: failure!\n return false;\n };\n\n const selectorArray = Array.isArray(selector) ? selector : topicToPath(selector);\n const pathArray = Array.isArray(topic) ? topic : topicToPath(topic);\n return byArray(selectorArray, pathArray);\n};\n\n/** sub is a strict sub-topic of parent, and in particular not equal */\nconst isSubTopicOf = (sub, parent) => {\n const pPath = topicToPath(parent);\n const sPath = topicToPath(sub);\n return isPrefixOf(pPath, sPath) && pPath.length < sPath.length;\n};\n\n/** prefixArray is a prefix of the array */\nconst isPrefixOf = (prefixArray, array) => {\n if (prefixArray.length == 0) return true;\n return (prefixArray[0] == array[0] &&\n isPrefixOf(prefixArray.slice(1), array.slice(1))\n );\n}\n\n\nmodule.exports = {\n topicToPath,\n pathToTopic,\n toFlatObject,\n forMatchIterator,\n setFromPath,\n encodeTopicElement,\n decodeTopicElement,\n topicMatch,\n isSubTopicOf,\n};", "module.exports = {\n rosReleases: {\n kinetic: { rosVersion: 1, ubuntuCodename: 'xenial' },\n melodic: { rosVersion: 1, ubuntuCodename: 'bionic' },\n noetic: { rosVersion: 1, ubuntuCodename: 'focal' },\n dashing: { rosVersion: 2 },\n eloquent: { rosVersion: 2 },\n foxy: { rosVersion: 2 },\n galactic: { rosVersion: 2 },\n humble: { rosVersion: 2 },\n iron: { rosVersion: 2 },\n jazzy: { rosVersion: 2 },\n kilted: { rosVersion: 2 },\n rolling: { rosVersion: 2 },\n }\n};\n", "const semverCompare = require('semver/functions/compare');\nconst semverMinVersion = require('semver/ranges/min-version');\n\nconst _ = {\n get: require('lodash/get'),\n set: require('lodash/set'),\n unset: require('lodash/unset'),\n forEach: require('lodash/forEach'),\n map: require('lodash/map'),\n isEmpty: require('lodash/isEmpty'),\n eq: require('lodash/isEqual'),\n isPlainObject: require('lodash/isPlainObject'),\n merge: require('lodash/merge'),\n};\n\nconst loglevel = require('loglevel');\nconst prefix = require('loglevel-plugin-prefix');\nconst chalk = require('chalk');\n\nconst { topicToPath, pathToTopic, toFlatObject, setFromPath, forMatchIterator,\n topicMatch, isSubTopicOf, encodeTopicElement, decodeTopicElement }\n = require('./datacache/tools');\n\nconst constants = require('./constants');\n\n// ----------------------------------------------------------------------------\n// Logging, incl. logger prefix\n\n/* Convenience function to set all loggers to the given level. */\nloglevel.setAll = (level) =>\n Object.values(loglevel.getLoggers()).forEach(l => l.setLevel(level));\n\nconst logColors = {\n warn: chalk.yellow,\n error: chalk.red,\n info: chalk.green,\n debug: chalk.gray,\n};\n\nconst levelFormatter =\n (level) => logColors[level] ? logColors[level](level) : level;\n\nprefix.reg(loglevel);\n\nif (typeof window != 'undefined') {\n // browser: keep it simple\n prefix.apply(loglevel, {\n template: '[%n %l]',\n });\n} else {\n // back-end + robot: include timestamp and use colors\n prefix.apply(loglevel, {\n template: '[%t %n %l]',\n levelFormatter,\n timestampFormatter: date => chalk.blue(date.toISOString()),\n });\n}\n\n/** Get a new loglevel logger; call with a name, e.g., `module.id`. The returned\n* logger has methods trace, debug, info, warn, error. See\n* https://www.npmjs.com/package/loglevel for details.\n*/\nconst getLogger = loglevel.getLogger;\n\n// ----------------------------------------------------------------------------\n\n/** Deep-clone the given object. All functionality is lost, just data is kept. */\nconst clone = (obj) => JSON.parse(JSON.stringify(obj));\n\n/** Parse JWT and return the decoded payload (JSON). */\nconst decodeJWT = (jwt) => JSON.parse(atob(jwt.split('.')[1]));\n// TODO: make this robust against bad JWTs (throw a more readable error)\n\n/** Try parsing JSON, return null if unsuccessful */\nconst tryJSONParse = (string) => {\n try {\n return JSON.parse(string);\n } catch (e) {\n return null;\n }\n};\n\n/** Reusable visitor pattern: iteratively visits all nodes in the tree\n described by `object`, where `childField` indicates the child-of predicate.\n*/\nconst visit = (object, childField, visitor) => {\n if (!object) return;\n visitor(object);\n object[childField]?.forEach(child => visit(child, childField, visitor));\n};\n\n/** Given an object and a path, visit each ancestor of the path */\nconst visitAncestor = (object, path, visitor, prefix = []) => {\n visitor(object, prefix);\n const next = path[0];\n if (next) {\n const sub = object[next];\n if (sub) {\n visitAncestor(sub, path.slice(1), visitor, prefix.concat(next));\n }\n }\n};\n\n/** Wait for delay ms, for use in async functions. */\nconst wait = (delay) => new Promise((resolve) => { setTimeout(resolve, delay); });\n\n\n\n\n// -------------------------------------------------------------------------\n// MQTT Tools\n\n/** parse usernames used in MQTT */\nconst parseMQTTUsername = (username) => {\n const parts = username.split(':');\n return {\n organization: parts[0],\n client: parts[1],\n sub: parts.slice(2)\n }\n};\n\n/** parse an MQTT topic according to our topic schema */\nconst parseMQTTTopic = (topic) => {\n const parts = topicToPath(topic);\n return {\n organization: parts[0],\n device: parts[1],\n capabilityScope: parts[2],\n capabilityName: parts[3],\n capability: `${parts[2]}/${parts[3]}`,\n version: parts[4],\n sub: parts.slice(5)\n }\n};\n\nconst mqttParsePayload = (payload) =>\n payload.length == 0 ? null : JSON.parse(payload.toString('utf-8'));\n// TODO: ^ This can probably now just become tryJSONParse\n\n/** delete all retained messages in a certain topic prefix, waiting for\n a given delay to collect existing retained. Use with care, never delete topics\n not owned by us. Harmless within capabilities, which are namespaced already.\n*/\nconst mqttClearRetained = (mqttClient, prefixes, callback, delay = 1000) => {\n\n const toDelete = [];\n const collectToDelete = (topic) => {\n // there may be other mqtt subscriptions running, filter by topic\n prefixes.forEach(prefix =>\n // mqttTopicMatch(topic, `${prefix}/#`) && toDelete.push(topic)\n topicMatch(`${prefix}/#`, topic) && toDelete.push(topic)\n );\n }\n mqttClient.on('message', collectToDelete);\n\n // subscribe to all\n prefixes.forEach(prefix => {\n if (typeof prefix == 'string') {\n mqttClient.subscribe(`${prefix}/#`);\n } else {\n console.warn('Ignoring', prefix, 'since it is not a string.');\n }\n });\n\n // value to use to clear, depending on node.js vs. browser\n const nullValue = (typeof Buffer != 'undefined' ? Buffer.alloc(0) : null);\n\n setTimeout(() => {\n mqttClient.removeListener('message', collectToDelete);\n prefixes.forEach(prefix => mqttClient.unsubscribe(`${prefix}/#`));\n\n const count = toDelete.length;\n console.log(`clearing ${count} retained messages from ${prefixes}`);\n toDelete.forEach(topic => {\n mqttClient.publish(topic, nullValue, {retain: true});\n });\n\n callback && callback(count);\n }, delay);\n};\n\n\n// WIP: a cleaner, more explicit way to serialize/deserialize mqtt payloads\n// /** Serialize a message for transport via MQTT */\n// const mqttSerialize = (message) => {\n// return value == null ? null : JSON.stringify(message);\n// };\n\n// /** Deserialize a message from MQTT */\n// const mqttDeserialize = (payload) => {\n// return payload.length == 0 ? null : JSON.parse(payload.toString('utf-8'));\n// };\n\n\n// -------------------------------------------------------------------------\n\n/** Generate a random id (base36) */\nconst getRandomId = (bytes = 6) => {\n const buffer = new Uint8Array(bytes);\n crypto.getRandomValues(buffer);\n return buffer.reduce((memo, i) => memo + i.toString(36), '');\n};\n\n/** Convert number to base52 [a-zA-Z] */\nconst toBase52 = (num) => {\n const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n const list = [];\n do {\n list.unshift(characters[num % 52]);\n num = Math.floor(num / 52);\n } while (num > 0);\n return list.join('');\n}\n\n/** Get a base52 representation [a-zA-Z] of the current date (ms since epoch) */\nconst getDateBase52 = () => toBase52(Date.now());\n\n// -------------------------------------------------------------------------\n\n/** Compare to version strings. Return -1 if a is lower than b,\n0 if they are equal, and 1 otherwise. If either is not a complete version,\ne.g., 2.0, interpret it as a range and use its minimum version for the\ncomparison. Hence, 2.0 < 2.0.1. */\nconst versionCompare = (a, b) =>\n semverCompare(semverMinVersion(a), semverMinVersion(b));\n\n// -------------------------------------------------------------------------\n\n/** given an object where the keys are versions, merge this into one object\n where the latest version of each subfield overwrites any previous */\nconst mergeVersions = (versionsObject, subTopic = undefined, options = {}) => {\n if (!versionsObject) {\n return subTopic ? _.set({}, subTopic, versionsObject) : versionsObject;\n }\n\n const versions = Object.keys(versionsObject).filter(ver =>\n (!options.maxVersion || versionCompare(ver, options.maxVersion) <= 0) &&\n (!options.minVersion || versionCompare(options.minVersion, ver) <= 0))\n .sort(versionCompare);\n\n const merged = {};\n const subPath = subTopic && topicToPath(subTopic);\n versions.forEach(nextVersion => {\n const newValue = subPath ? _.get(versionsObject[nextVersion], subPath) :\n versionsObject[nextVersion];\n // Object.assign(merged, newValue);\n _.merge(merged, newValue);\n });\n return subPath ? _.set({}, subPath, merged) : merged;\n};\n\n// -------------------------------------------------------------------------\n// Formatting tools\n\nconst units = ['B', 'KB', 'MB', 'GB', 'TB'];\nconst formatBytes = (bytes) => {\n if (!bytes) return '--';\n let i = 0;\n while (bytes > 1024) {\n bytes /= 1024;\n i++;\n }\n return `${bytes.toFixed(2)} ${units[i]}`;\n}\n\nconst formatDuration = (seconds) => {\n if (!seconds) return '--';\n const parts = {};\n if (seconds > 3600) {\n parts.h = Math.floor(seconds / 3600);\n seconds = seconds % 3600;\n }\n if (seconds > 60) {\n parts.m = Math.floor(seconds / 60);\n seconds = seconds % 60;\n }\n parts.s = Math.floor(seconds);\n\n let rtv = '';\n parts.h > 0 && (rtv += `${parts.h}h `);\n parts.m > 0 && (rtv += `${parts.m}m `);\n !parts.h && (rtv += `${parts.s}s`);\n return rtv.trim();\n};\n\n// -------------------------------------------------------------------------\n\nmodule.exports = { parseMQTTUsername, parseMQTTTopic,\n pathToTopic, topicToPath, toFlatObject, topicMatch,\n mqttParsePayload, getRandomId, toBase52, getDateBase52, versionCompare,\n loglevel, getLogger,\n mergeVersions, mqttClearRetained, isSubTopicOf, clone, setFromPath,\n forMatchIterator, encodeTopicElement, decodeTopicElement, constants, visit,\n wait, formatBytes, formatDuration, tryJSONParse,\n decodeJWT, visitAncestor\n};\n", "\nconst _ = {\n get: require('lodash/get'),\n set: require('lodash/set'),\n unset: require('lodash/unset'),\n forEach: require('lodash/forEach'),\n map: require('lodash/map'),\n isEmpty: require('lodash/isEmpty'),\n eq: require('lodash/isEqual'),\n isPlainObject: require('lodash/isPlainObject'),\n merge: require('lodash/merge'),\n};\n\nconst {topicToPath, pathToTopic, toFlatObject, topicMatch, forMatchIterator}\n = require('./tools');\n\n/** Unset the topic in that obj, and clean up parent if empty, recursively.\nReturn the path to the removed node.\n*/\nconst unset = (obj, path) => {\n if (!path || path.length == 0) return;\n _.unset(obj, path);\n const parentPath = path.slice(0, -1);\n // _.get doesn't do the intuitive thing for the empty path, handle it ourselves\n const parent = parentPath.length == 0 ? obj : _.get(obj, parentPath);\n if (_.isEmpty(parent)) {\n return unset(obj, parentPath);\n } else {\n return path;\n }\n};\n\n/** Given a modifier `{\"a/b/c\": \"xyz\"}` update the object `obj` such that\n`obj.a.b.c = \"xyz\"`. */\nconst updateObject = (obj, modifier) => {\n _.forEach( modifier, (value, topic) => {\n const path = topicToPath(topic);\n if (value == null) {\n unset(obj, path);\n } else {\n _.set(obj, path, value);\n }\n });\n return obj;\n};\n\n/** Given an object and a path with wildcards (`*` and `+`), *modify* the object\nto only contain elements matched by the path, e.g.,\n`{a: {b: 1, c: 2}, d: 2}` and `['a','+']` would give `{a: {b: 1, c: 2}}`\n\n@param {object} obj - The object to select from\n@param {array} path - An array specifying the path to select, potentially\ncontaining mqtt wildcards ('+').\n*/\nconst selectFromObject = (obj, path) => {\n if (path.length == 0) return;\n const next = path[0];\n if (next) {\n for (let key in obj) {\n if (key != next && next != '*' && !next.startsWith('+')) {\n delete obj[key];\n } else {\n selectFromObject(obj[key], path.slice(1));\n }\n }\n }\n};\n\n\n/**\n* A class implementing a local data cache, used as a local data store with\n* deduplication detection and update events. While this class is very handy\n* you probably won't need to create instances of it directly. Instead use\n* the mqttSync.data instance which holds the locally stored data\n* subscribed/published from/to MQTTSync.\n* For example on the robot:\n* ```js\n* // update/publish our status:\n* mqttSync.data.update('status', {changed: Date.now(), msg: 'OK'});\n* // subscribe to new user requests (e.g., from UI):\n* mqttSync.data.subscribePath('+user/request', (request, key, {user}) => {\n* log.debug(`user ${user} made request`, request);\n* });\n* ```\n* In the cloud or in a web component you would need to use the full topic including\n* org, device, scope, cap-name, and version.\n*/\nclass DataCache {\n\n #data = {};\n #listeners = [];\n #flatListeners = [];\n\n constructor(data = {}) {\n this.#data = data;\n\n // for now add an alias\n this.subscribeTopic = this.subscribePath;\n }\n\n /** Update the object with the given value at the given path, remove empty;\n return the flat changes (see toFlatObject). Add `tags` to updates to mark\n them somehow based on the context, e.g., so that some subscriptions can choose\n to ignore updates with a certain tag.\n */\n updateFromArray(path, value, tags = {}) {\n // const empty = Object.keys(this.#data).length == 0; // object already empty\n const current = _.get(this.#data, path);\n if (value == null) {\n if (current === undefined || current === null) {\n return {}; // no change, do not call listeners\n } else {\n unset(this.#data, path);\n }\n } else {\n if (_.eq(current, value)) {\n // note: this is just a shallow equal, so replacing a sub-document\n // with an atomic copy of it should still trigger listeners.\n // TODO: Note also that when value is an object, we will set it by\n // reference here, so any changes made to that object will *not* trigger\n // listeners because `current` will already be changed -- which is\n // probably wrong. May want to always clone value first.\n return {}; // nothing to do, do not bother listeners\n }\n // console.log('setting', path, value);\n _.set(this.#data, path, value);\n // TODO: implement this ourselves so we can do better change-checking\n }\n\n const topic = pathToTopic(path);\n const obj = {[topic]: value};\n\n // flatten the value and combine eith topic (without reflattening the topic):\n let flatChanges;\n if (value instanceof Object) {\n const flatValue = toFlatObject(value);\n flatChanges = {};\n _.forEach(flatValue, (atomic, flatKey) => {\n flatChanges[`${topic}${flatKey}`] = atomic;\n });\n } else {\n flatChanges = obj;\n }\n\n // option 1. using flat changes (sub-documents are never atomic)\n // this.#listeners.forEach(fn => fn(flatChanges));\n\n // option 2. allow atomic sub-document changes\n this.#listeners.forEach(fn => fn(obj, tags));\n\n this.#flatListeners.forEach(fn => fn(flatChanges, tags));\n\n return flatChanges;\n }\n\n /** Update the value at the given path (array or dot separated string) */\n update(path, value, tags) {\n if (typeof path == 'string') {\n return this.updateFromTopic(path, value, tags);\n } else if (path instanceof Array) {\n return this.updateFromArray(path, value, tags);\n } else {\n throw new Error('unrecognized path expression');\n }\n }\n\n /** Set value from the given topic (with or without leading or trailing slash) */\n updateFromTopic(topic, value, tags) {\n return this.updateFromArray(topicToPath(topic), value, tags);\n }\n\n /** Update data from a modifier object where keys are topic names to be\n interpreted as paths, and values are the values to set */\n updateFromModifier(modifier, tags) {\n return _.map(modifier, (value, topic) =>\n this.updateFromTopic(topic, value, tags));\n }\n\n /** Add a callback for all change events. */\n subscribe(callback) {\n if (callback instanceof Function) {\n this.#listeners.push(callback);\n } else {\n console.warn('DataCache.subscribe expects a function as argument. Did you mean to use subscribePath?');\n }\n }\n\n /** Subscribe to a specific path (array) only. Callback receives\n `value, key, matched, tags`. */\n subscribePath(path, callback) {\n this.#listeners.push((changes, tags) => {\n _.forEach(changes, (value, key) => {\n const matched = topicMatch(path, key);\n matched && callback(value, key, matched, tags);\n });\n });\n }\n\n /** Subscribe to a specific topic only. Callback receives\n `value, key, matched, tags`. */\n subscribeTopic(topic, callback) {\n this.subscribePath(topic, callback);\n }\n\n /** Same as subscribePath but always get all changes in flat form */\n subscribePathFlat(topic, callback) {\n this.#flatListeners.push((changes, tags) => {\n _.forEach(changes, (value, key) => {\n const matched = topicMatch(topic, key);\n matched && callback(value, key, matched, tags);\n });\n });\n }\n\n /** Remove a callback previously registered using `subscribe`. */\n unsubscribe(callback) {\n this.#listeners = this.#listeners.filter(f => f != callback);\n }\n\n /** Get sub-value at path, or entire object if none given */\n get(path = []) {\n return path.length == 0 ? this.#data : _.get(this.#data, path);\n }\n\n /** Get sub-value specified by topic */\n getByTopic(topic) {\n return this.get(topicToPath(topic));\n }\n\n /** Filter the object using path with wildcards */\n filter(path) {\n const rtv = JSON.parse(JSON.stringify(this.get()));\n selectFromObject(rtv, path);\n return rtv;\n }\n\n /** Filter the object using topic with wildcards */\n filterByTopic(topic) {\n return this.filter(topicToPath(topic));\n }\n\n /** For each topic match, invoke the callback with the value, path, and match\n just like subscribePath, but on the current data rather than future changes. */\n forMatch(topic, callback) {\n const path = topicToPath(topic);\n this.forPathMatch(path, callback);\n }\n\n /** For each path match, invoke the callback with the value, path, and match\n just like subscribePath */\n forPathMatch(path, callback) {\n forMatchIterator(this.get(), path, callback);\n }\n};\n\nmodule.exports = {\n DataCache, updateObject\n}", "const dataCache = require('./DataCache');\nconst tools = require('./tools');\n\nmodule.exports = { ...dataCache, ...tools };", "'use strict';\n\nconst _ = require('lodash');\n\nconst { mqttParsePayload, topicMatch, topicToPath, pathToTopic,\ntoFlatObject, getLogger, mergeVersions, parseMQTTTopic, isSubTopicOf,\nversionCompare, encodeTopicElement, visitAncestor, getRandomId }\n = require('./common');\nconst { DataCache } = require('./datacache/DataCache');\n\n\nconst log = getLogger('MqttSync');\nlog.setLevel('info');\n\nconst HEARTBEAT_TOPIC = '$SYS/broker/uptime';\nconst specialKey = '$_'; // special key to reify \"value\" in publishedMessages\n\nconst noop = () => {};\n\n/* clone a mqtt payload, if necessary */\nconst clone = (payload) => {\n if (typeof payload == 'object') {\n return JSON.parse(JSON.stringify(payload));\n } else {\n return payload;\n }\n};\n\n/* return new string that ends in /# for sure */\nconst ensureHashSuffix = (topic) =>\n topic.endsWith('/#') ? topic :\n ( topic.endsWith('/') ? topic.concat('#') :\n topic.concat('/#') );\n\n/* given a path, replace any double slashes, '//', with single ones */\nconst resolveDoubleSlashes = (path) => path.replace(/\\/\\//g, '/');\n\n\n/** A class that combines DataCache and MQTT to implement a data synchronization\nfeature over the latter. Relies on retained messages in mqtt for persistence.\n* @param {object} options\n* @param {object} options.mqttClient - An already connected mqtt.js client.\n* @param {boolean} [options.ignoreRetain] - retain all messages, ignorant of the retain\n* flag.\n* @param {number} [options.sliceTopic] - a number indicating at what level to\n* slice the topic, i.e., only use a suffix. Used in robot-capabilities to slice\noff the topic prefix (namespaces).\n* @param {array} [options.migrate] - an array of objects of the form\n* `{topic, newVersion, level}`. Only meaningful in the cloud. Instructs MQTTSync\nto first migrate existing topics to a new version namespace, publishing at the\ndesignated level down from the version level. For example:\n```js\n[{ topic: `/myorg/mydevice/@local/my-cap/+/config`,\n newVersion: this.version,\n level: 1\n}]\n```\nWould migrate any existing data in the capability's `config` namespace to the\ncurrent version of the package, publishing at the `config/+` level (rather than\natomically at the config level itself).\n* @param {function} [options.onReady] - A function that is called when the MQTTSync\nclient is ready and has completed any requested migrations.\n* @param {function} [options.onChange] - A function that is called any time there\nis a change to the shared data. This is not usually used. It's usually better to\nuse the finer grained `MqttSync.data.subscribePath` instead, that allows you to\nsubscribe to changes just on a specific sun-object instead, see DataCache.\n*/\nclass MqttSync {\n\n data = new DataCache();\n\n /* Directory of paths we've subscribed to in this class; this matters\n because the same mqtt client may have subscriptions to paths that we don't\n care to store (sync). */\n subscribedPaths = {};\n\n publishedPaths = {}; // not used in atomic mode\n\n /* Store messages retained on mqtt so we can publish what is necessary to\n achieve the \"should-be\" state. Note that we cannot use a structured document\n for storing these publishedMessages since we need to be able to store separate\n values at non-leaf nodes in the object (just like mqtt, where you can have\n /a/b = 1 and /a/b/c = 1 at the same time). Note: not used in atomic mode.\n Note: we use specialKey in this DataCache to allow overlapping\n topics (e.g., `/a/b/$_ = 1` and `/a/$_ = {b: 2}`)\n */\n publishedMessages = new DataCache();\n\n /* The order in which we send retained messages matters, which is why we use\n a queue for sending things. Note that we here use the property of Map that it\n remembers insertion order of keys. */\n publishQueue = new Map();\n\n /* We need to keep a record of all received topics (not messages) so far in\n case we want to clear any of them. */\n receivedTopics = new Set();\n\n /* List of callbacks waiting for next heartbeat, gets purged with each\n heartbeat */\n heartbeatWaitersOnce = [];\n\n heartbeats = 0;\n\n beforeDisconnectHooks = [];\n\n rpcHandlers = {}; // handlers for incoming RPC requests\n rpcCallbacks = {}; // callback for RPC requests we've sent\n\n constructor({mqttClient, onChange, ignoreRetain, migrate, onReady,\n sliceTopic, onHeartbeatGranted }) {\n\n this.mqtt = mqttClient;\n this.sliceTopic = sliceTopic;\n\n this.mqtt.on('message', (topic, payload, packet) => {\n const payloadString = payload && payload.toString()\n // log.debug('got message', topic, payloadString.slice(0, 180),\n // payloadString.length > 180 ? `... (${payloadString.length} bytes)` : '',\n // packet.retain);\n\n if (topic == HEARTBEAT_TOPIC) {\n if (this.heartbeats > 0) { // ignore initial heartbeat (retained)\n this.heartbeatWaitersOnce.forEach(cb => cb());\n this.heartbeatWaitersOnce = [];\n }\n if (this.heartbeats == 1 && !migrate && onReady) onReady();\n this.heartbeats++;\n\n } else {\n this.receivedTopics.add(topic);\n // Do NOT parse payload just yet, since it may be binary and ignored by us\n\n let path = topicToPath(topic);\n log.debug('processing message', topic);\n if (sliceTopic) {\n path = path.slice(sliceTopic);\n topic = pathToTopic(path);\n }\n\n if (this.rpcHandlers[topic]) {\n const json = mqttParsePayload(payload);\n this.handleRPCRequest(topic, json);\n\n } else if (this.rpcCallbacks[topic]) {\n const json = mqttParsePayload(payload);\n this.handleRPCResponse(topic, json);\n\n } else if (packet.retain || ignoreRetain) {\n\n if (this.isPublished(topic)) {\n const json = mqttParsePayload(payload);\n // store plain messages, still stored in a structure, but values are\n // not interpreted; we just store them to undo them if necessary, e.g.,\n // for switching between atomic and non-atomic subdocuments\n // log.trace('setting publishedMessages', topic);\n this.publishedMessages.updateFromArray([...path, specialKey], json);\n\n // this.pubData.update(topic, json);\n // Still need to update the data so that we can detect changes we make\n // and publish them. But we need to break the reaction-chain to avoid\n // loops, so tag this update with 'published' and then ignore those\n // updates in this.publish.\n this.data.update(topic, json, {external: true});\n\n } else if (this.isSubscribed(topic)) {\n const json = mqttParsePayload(payload);\n\n log.debug('applying received update', topic);\n const changes = this.data.update(topic, json, {external: true});\n onChange && Object.keys(changes).length > 0 && onChange(changes);\n }\n }\n // else: do not try to parse it, it might be a binary message sent\n // directly using the client (with or without retain)\n }\n });\n\n this.mqtt.subscribe(HEARTBEAT_TOPIC, {rap: true}, (err, granted) => {\n log.debug(HEARTBEAT_TOPIC, {granted});\n granted && granted.length > 0 && onHeartbeatGranted?.();\n });\n\n migrate?.length > 0 && this.migrate(migrate, () => {\n log.debug('done migrating');\n onReady && this.waitForHeartbeatOnce(onReady);\n });\n }\n\n /**\n * Publish all values at the given level of the given object under the given\n * topic (plus sub-key, of course).\n * TODO: Is this OK, or do we need to go through this.publish?\n */\n publishAtLevel(topic, value, level) {\n log.debug(`publishingAtLevel ${level}`, topic, value);\n\n if (level > 0) {\n _.forEach(value, (subValue, subKey) => {\n const subTopic = `${topic}/${encodeTopicElement(subKey)}`;\n log.debug(`publishing ${subTopic}`);\n this.publishAtLevel(subTopic, subValue, level - 1);\n });\n } else {\n this.mqtt.publish(topic, JSON.stringify(value), {retain: true}, (err) => {\n err && log.warn('Error when publishing migration result', err);\n });\n }\n }\n\n /** Migrate a list of `{topic, newVersion, transform}`. The version number in\n * topic will be ignored, and all versions' values will be merged, applied in\n * order, such that the latest version is applied last. `topic` may include\n * wildcards in the part before the version number but not after.\n *\n * Example:\n * ```js\n * mqttSync.migrate([{topic: '/+/dId/@scope/capname/+/b', newVersion: '1.2.0'}]\n * ```\n */\n migrate(list, onReady = undefined) {\n\n let toGo = list.length;\n if (toGo == 0) {\n onReady && onReady(); // in case an empty list was given\n return;\n }\n\n /* called each time one item is done */\n const oneDown = () => --toGo == 0 && onReady && onReady();\n\n list.forEach(({topic, newVersion, transform = undefined, flat = false,\n level = 0}) => {\n log.debug('migrating', topic, newVersion);\n const {organization, device, capability, sub} = parseMQTTTopic(topic);\n const prefix = `/${organization}/${device}/${capability}`;\n\n const suffix = sub.length == 0 ? '/#' : pathToTopic(sub);\n // suffix will have a leading slash\n const subTopic = `${prefix}/+${suffix}`;\n\n this.subscribe(subTopic, (err) => {\n if (err) {\n log.warn('Error during migration', err);\n oneDown();\n return;\n }\n\n const all = {};\n this.waitForHeartbeatOnce(() => {\n\n // for each match (prefix can include wildcards) merge everything\n this.data.forMatch(prefix, (value, path, match) => {\n // an actual (ground, aka. no wildcard) prefix\n const groundPrefix = pathToTopic(path);\n\n log.debug('got heartbeat', {prefix, topic, subTopic, suffix}, groundPrefix, value);\n if (!value) {\n // no data to migrate\n return;\n }\n // collect for cleanup\n Object.assign(all, value);\n\n const merged = mergeVersions(value, suffix, {maxVersion: newVersion});\n // get suffix in merged\n const suffixMergedValue = _.get(merged, topicToPath(suffix));\n log.debug({value, suffix, merged, suffixMergedValue});\n // ^ this will need to change to support wild-cards in suffix\n const transformed = transform ? transform(suffixMergedValue) :\n suffixMergedValue;\n\n // Publish the transformed value under the ground prefix as\n // `newVersion/suffix`\n const newTopic =\n resolveDoubleSlashes(`${groundPrefix}/${newVersion}/${suffix}`);\n log.debug('publishing merged', newTopic);\n\n if (flat) {\n const flatObj = toFlatObject(transformed);\n const newPath = topicToPath(newTopic);\n _.forEach(flatObj, (value, key) => {\n const keyTopic = pathToTopic(newPath.concat(topicToPath(key)));\n // TODO: Is this OK, or do we need to go through this.publish?\n this.mqtt.publish(keyTopic, JSON.stringify(value),\n {retain: true}, (err) => {\n err && log.warn(\n `Error when publishing migration result for ${key}`, err);\n });\n });\n\n } else {\n this.publishAtLevel(newTopic, transformed, level);\n }\n });\n\n this.unsubscribe(subTopic);\n\n if (Object.keys(all).length == 0) {\n // no data to migrate\n oneDown();\n return;\n }\n\n this.waitForHeartbeatOnce(() => {\n // now clear this suffix in the old version space\n const oldVersions = Object.keys(all).filter(v =>\n versionCompare(v, newVersion) < 0);\n // log.debug({oldVersions});\n\n const prefixesToClear = oldVersions.map(oldV =>\n resolveDoubleSlashes(`${prefix}/${oldV}/${suffix}`));\n\n this.clear(prefixesToClear);\n oneDown();\n });\n });\n });\n });\n }\n\n /** Delete all retained messages in a certain topic prefix, waiting for\n a mqtt broker heartbeat to collect existing retained. Use with care, never\n delete topics not owned by us. Harmless within capabilities, which are\n namespaced already.\n\n `options.filter(topic)`: a function that can be provided to further,\n programmatically filter the set of topics to clear, e.g., to onlt clear\n topics of old versions.\n\n Note: This may not yet work in robot-capabilities, since the subscription\n prefix and received topic prefix don't match (the device prefix is added to\n subscription by localMQTT.\n */\n clear(prefixes, callback = undefined, options = {}) {\n\n // If prefixes are empty, just skip (call callback and return)\n if (!prefixes || prefixes.length == 0) {\n log.warn('clear was given no prefixes');\n callback?.(0);\n return;\n }\n\n const toDelete = [];\n const collectToDelete = (topic) => {\n // there may be other mqtt subscriptions running, filter by topic\n prefixes.forEach(prefix =>\n topicMatch(`${prefix}/#`, topic)\n && (!options.filter || options.filter(topic))\n && toDelete.push(topic)\n );\n }\n this.mqtt.on('message', collectToDelete);\n // this only collects new topics, not those we've already received\n\n // subscribe to all\n prefixes.forEach(prefix => {\n if (typeof prefix == 'string') {\n this.mqtt.subscribe(`${prefix}/#`);\n } else {\n log.warn('Ignoring', prefix, 'since it is not a string.');\n }\n });\n\n // add matching topics we already know off:\n this.receivedTopics.forEach(collectToDelete);\n\n // value to use to clear, depending on node.js vs. browser\n const nullValue = (typeof Buffer != 'undefined' ? Buffer.alloc(0) : null);\n\n this.waitForHeartbeatOnce(() => {\n this.mqtt.removeListener('message', collectToDelete);\n prefixes.forEach(prefix => this.mqtt.unsubscribe(prefix));\n\n const count = toDelete.length;\n log.info(`clearing ${count} retained messages from ${prefixes}`);\n toDelete.forEach(topic => {\n this.mqtt.publish(topic, nullValue, {retain: true});\n });\n\n callback && callback(count);\n });\n };\n\n\n /** register a callback for the next heartbeat from the broker */\n waitForHeartbeatOnce(callback) {\n // need to wait a tick, in case we are still in the callback tree\n // of a previous heartbeat waiter\n setTimeout(() => this.heartbeatWaitersOnce.push(callback), 1);\n }\n\n /** check whether we are subscribed to the given topic */\n isSubscribed(topic) {\n return Object.keys(this.subscribedPaths).some(subscribedTopic =>\n topicMatch(subscribedTopic, topic));\n }\n\n /** Check whether we are publishing the given topic in a non-atomic way.\n This is used to determine whether to store the published value or not. */\n isPublished(topic) {\n return Object.keys(this.publishedPaths).some(subscribedTopic =>\n topicMatch(subscribedTopic, topic) &&\n !this.publishedPaths[subscribedTopic].atomic\n );\n }\n\n /** Subscribe to the given topic (and all sub-topics). The callback will\n indicate success/failure, *not* a message on the topic. */\n subscribe(topic, callback = noop) {\n topic = ensureHashSuffix(topic);\n log.debug('subscribing to', topic);\n if (this.subscribedPaths[topic]) {\n log.debug('already subscribed to', topic);\n callback();\n return;\n }\n\n this.mqtt.subscribe(topic, {rap: true}, (err, granted) => {\n log.debug('subscribe', topic, 'granted:', granted);\n if (granted && granted.some(grant => grant.topic == topic && grant.qos < 128)) {\n // granted\n this.subscribedPaths[topic] = 1;\n callback(null);\n } else {\n // let user know (somehow) when we don't get permission\n callback(`not permitted to subscribe to topic ${topic}, ${JSON.stringify(granted)}`);\n }\n });\n }\n\n unsubscribe(topic) {\n topic = ensureHashSuffix(topic);\n if (this.subscribedPaths[topic]) {\n this.mqtt.unsubscribe(topic);\n delete this.subscribedPaths[topic];\n }\n }\n\n /** Publish retained to MQTT, store as published, and return a promise */\n _actuallyPublish(topic, value) {\n // return new Promise((resolve, reject) =>\n // this.mqtt.publish(topic,\n // value == null ? null : JSON.stringify(value), // aka \"unparse payload\"\n // {retain: true},\n // (err) => {\n // // Note that this returns optimistically at QoS 0, and no error occurs\n // // even when we are not allowed to publish this topic/message, see\n // // https://github.com/mqttjs/MQTT.js/#publish. Only when the client\n // // disconnects it seems.\n // if (err) {\n // log.warn('error in _actuallyPublish:', err);\n // reject(err);\n // // TODO: if this happens, we may need to force a full-sync\n // } else {\n // resolve();\n // }\n // }));\n\n if (!this.mqtt.connected) {\n log.warn('not connected, not publishing', topic);\n return false;\n }\n log.debug('actually publishing', topic);\n this.mqtt.publish(topic,\n value == null ? null : JSON.stringify(value), // aka \"unparse payload\"\n {retain: true});\n return true;\n }\n\n /** Send all items in the queue in sequence, if any and if not already\n running. */\n // async _processQueue() {\n // if (this._processing) return; // already running (and probably waiting)\n //\n // this._processing = true;\n // while (this.publishQueue.length > 0) {\n // const {topic, value} = this.publishQueue.shift();\n // await this._actuallyPublish(topic, value);\n // }\n // this._processing = false;\n // }\n\n // when using Map\n _processQueue_rec(cb) {\n if (this.publishQueue.size > 0) {\n const [topic, value] = this.publishQueue.entries().next().value;\n // this.publishQueue.delete(topic);\n // this._actuallyPublish(topic, value).then(\n // () => this._processQueue_rec(cb),\n // cb); // always call cb, even in rejection case\n if (this._actuallyPublish(topic, value)) {\n this.publishQueue.delete(topic);\n this._processQueue_rec(cb);\n } else {\n // try again soon\n setTimeout(() => this._processQueue_rec(cb), 5000);\n }\n } else {\n cb();\n }\n }\n\n _processQueue() {\n if (this._processing) return; // already running (and probably waiting)\n\n this._processing = true; // semaphore\n this._processQueue_rec(() => this._processing = false);\n }\n\n /** Set delay between processing of publishing queue in milliseconds. This\n allows you to effectively throttle the rate at which this instance will\n publish changes. Note that updates to a topic already in the queue will not\n cause multiple publications. Only the latest value will be published.\n @param {number} [delay] - Number of milliseconds to wait between processing\n of publish queue.\n */\n setThrottle(delay) {\n this._processQueueThrottled =\n _.throttle(this._processQueue.bind(this), delay);\n }\n\n /** Clear the set throttling delay. */\n clearThrottle() {\n delete this._processQueueThrottled;\n }\n\n addToQueue(topic, value) {\n // this.publishQueue.push({topic, value});\n this.publishQueue.set(topic, value);\n }\n\n /** Add to publication queue */\n _enqueue(topic, value) {\n log.debug('enqueuing', topic);\n this.addToQueue(topic, value);\n if (this._processQueueThrottled) {\n this._processQueueThrottled();\n } else {\n this._processQueue();\n }\n // yes, this is optimistic, but if we don't, then upcoming changes\n // may not work as expected (e.g., when switching from flat to atomic to flat)\n const path = topicToPath(topic);\n this.publishedMessages.updateFromArray([...path, specialKey],\n value == null ? null : clone(value));\n }\n\n /** Register a listener for path in data. Make sure to populate the data\n before calling this or set the data all at once afterwards.\n\n With option \"atomic\" this will always send the whole sub-document,\n not flat changes. Useful, e.g., for desiredPackages, see\n https://github.com/chfritz/transitive/issues/85.\n\n @return true if publication added (false, e.g., when already present)\n */\n publish(topic, options = {atomic: false}) {\n topic = ensureHashSuffix(topic);\n\n if (_.isEqual(this.publishedPaths[topic], options)) {\n return false;\n // avoid double subscription\n }\n this.publishedPaths[topic] = options;\n\n if (options.atomic) {\n // this case is quite simple\n this.data.subscribePath(topic, (value, key, matched, tags) => {\n // do not re-publish changes received from external:\n if (tags?.external) return;\n\n log.debug('processing change (atomic)', key, topic);\n // instantiate topic according to key (topic may have wildcards)\n const topicWithoutHash = topic.slice(0, topic.length - 2);\n const groundedTopic = pathToTopic(\n // get length of topic (how many levels of selectors), get that many\n // levels from key prefix\n topicToPath(key).slice(0, topicToPath(topicWithoutHash).length)\n );\n this._enqueue(groundedTopic, this.data.getByTopic(groundedTopic));\n });\n return true;\n }\n\n this.mqtt.subscribe(topic);\n\n // second: keep them up to date by publishing updates as changes happen\n this.data.subscribePath(topic, (value, key, matched, tags) => {\n if (tags?.external) return;\n\n log.debug('processing change', key);\n\n /* First: establish clarity by ensuring that the object defined by the\n currently present retained messages under this path accurately reflect\n the current value of this.data (and if not, publish what is necessary to\n create consistency). */\n // first, clear/replace all messages below or above this sub-path (if any)\n const path = topicToPath(key);\n\n // Check flat to atomic\n const publishedSub = this.publishedMessages.get(path);\n _.each(publishedSub, (oldSubVal, oldSubKey) => {\n if (oldSubKey == specialKey) return true;\n // We are going from flat to atomic, i.e., we are publishing at a\n // higher level than before: clear out old sub-keys.\n\n // Find all sub-sub-keys that end in `specialKey`:\n const toClear = Object.keys(toFlatObject(oldSubVal))\n .filter(subkey => subkey.endsWith(specialKey));\n\n log.debug('flat->atomic: ', {toClear}, oldSubKey);\n // Clear them all:\n toClear.forEach(oldSubSubKey => {\n const oldKey = oldSubSubKey.slice(0, -(specialKey.length + 1));\n const clearKey = `${key}/${oldSubKey}/${oldKey}`\n // log.debug('flat->atomic: clear', clearKey);\n this._enqueue(clearKey, null);\n });\n });\n\n // Check atomic to flat\n const published = this.publishedMessages.get();\n visitAncestor(published, path.slice(0, -1), (subObj, prefix) => {\n const oldVal = subObj[specialKey];\n if (oldVal && _.isObject(oldVal)) {\n log.debug('atomic->flat', {oldVal});\n // A parent topic has been published. We are going from atomic to\n // separate values: need to transform existing sub-document to flat\n // values.\n\n // Remove the old published (atomic) message:\n const prefixTopic = pathToTopic(prefix);\n this._enqueue(prefixTopic, null);\n\n // Now re-add as separate flat messages\n const flat = toFlatObject(oldVal);\n _.each(flat, (flatValue, flatKey) => {\n const oldFlatKey = `${prefixTopic}${flatKey}`;\n this._enqueue(oldFlatKey, flatValue);\n })\n }\n });\n\n /* We need to first wait until all of the above messages are out;\n otherwise replacing an atomic `/a = {c: 1}` with `/a/c = 2` would create\n a race condition where it is not clear which message, the replacement\n c = 1 or the new c = 2, would be sent last (and hence retained). That's\n why we use a publishing queue in this class.\n */\n this._enqueue(key, value);\n return true;\n });\n }\n\n /** Run all registered hooks before disconnecting */\n beforeDisconnect() {\n this.beforeDisconnectHooks.forEach(fn => fn(this));\n }\n\n /** Register a new hook to be called before disconnecting */\n onBeforeDisconnect(fn) {\n this.beforeDisconnectHooks.push(fn);\n }\n\n /* --------------------------------------------------------------------------\n * Remote Procedure Calls (RPC)\n */\n\n /* Handle RPC requests */\n async handleRPCRequest(topic, json) {\n log.debug('handling RPC request for', topic, json);\n const handler = this.rpcHandlers[topic];\n const result = handler(json.args);\n\n const responseTopic = `${topic.replace('/request', '/response')}/${json.id}`;\n\n if (result instanceof Promise) {\n result.then( resultValue => this.mqtt.publish(responseTopic,\n JSON.stringify({ id: json.id, result: resultValue }),\n {retain: false, qos: 2}));\n } else {\n this.mqtt.publish(responseTopic,\n JSON.stringify({ id: json.id, result }),\n {retain: false, qos: 2});\n }\n }\n\n /* Handle RPC response */\n handleRPCResponse(topic, json) {\n log.debug('handle RPC response', topic, json);\n this.rpcCallbacks[topic](json.result);\n delete this.rpcCallbacks[topic];\n this.mqtt.unsubscribe(topic);\n }\n\n /** Register an RPC request handler. Example:\n * ```js\n * mqttSync.register('/mySquare', arg => {\n * log.debug('running /mySquare with args', arg);\n * return arg * arg;\n * });\n * ```\n * Note that the command topic needs to be in the capabilities namespace like\n * any other topic. In robot capabilities, as usual, these can start in `/`\n * because the local mqtt bridge operated by the robot agent will place all\n * topics in their respective namespace. In the cloud and on the web you will\n * need to use the respective namespace, i.e.,\n * `/orgId/deviceId/@scope/capName/capVersion/`.\n *\n * #### Async/Await\n * Yes, you can make the handler `async` and use `await` inside of it. This\n * will be handled correctly, i.e., MqttSync will await the result of the\n * handler before responding to the RPC request client.\n */\n register(command, handler) {\n log.debug('registering RPC handler for', command);\n const requestTopic = `${command}/request`;\n\n this.rpcHandlers[requestTopic] = handler;\n this.mqtt.subscribe(requestTopic, {rap: true, qos: 2}, (err, granted) => {\n if (err) {\n log.warn(`Error subscribing to RPC topic ${requestTopic}`, err);\n } else if (granted && granted.length == 0) {\n log.warn(`Not allowed to subscribe to RPC topic ${requestTopic}`);\n }\n });\n }\n\n /** Make an RPC request. Example:\n * ```js\n * mqttSync.call('/mySquare', 11, result => {\n * log.debug(`Called /mySquare with arg 11 and got ${result}`);\n * });\n * ```\n * Alternative you can omit the callback and use async/await:\n * ```js\n * const result = await mqttSync.call('/mySquare', 11);\n * log.debug(`Called /mySquare with arg 11 and got ${result}`);\n * ```\n * See the note about namespaces in `register`.\n *\n * Note: It is your responsibility to only call methods that exist (have been\n * registered). Calling a non-existent command just hangs.\n */\n call(command, args, callback = undefined) {\n const id = getRandomId();\n\n const responseTopic = `${command}/response/${id}`;\n this.mqtt.subscribe(responseTopic, {rap: true, qos: 2}, (err, granted) => {\n if (err) {\n log.warn(`Error subscribing to RPC response topic ${responseTopic}`, err);\n } else if (granted && granted.length == 0) {\n log.warn(`Not allowed to subscribe to RPC response topic ${responseTopic}`);\n }\n });\n\n const requestTopic = `${command}/request`\n log.debug('calling RPC', requestTopic);\n this.mqtt.publish(requestTopic, JSON.stringify({ id, args }),\n {retain: false, qos: 2});\n\n if (callback) {\n this.rpcCallbacks[responseTopic] = callback;\n } else {\n return new Promise((resolve, reject) => {\n this.rpcCallbacks[responseTopic] = resolve;\n });\n }\n }\n}\n\nmodule.exports = MqttSync;\n", "export * from './client/shared.jsx';\nexport * from './client/hooks.jsx';\nexport * from './client/client';\n", "import React, { useState, useEffect, useMemo, useRef } from 'react';\nimport { Button, Accordion, AccordionContext, Card, Badge }\n from 'react-bootstrap';\nimport ReactWebComponent from './react-web-component';\n\nimport { parseCookie, decodeJWT } from './client';\nimport { useCapability } from './hooks';\n\nconst styles = {\n badge: {\n width: '4em'\n },\n code: {\n color: '#700',\n borderLeft: '3px solid #aaa',\n padding: '0.5em 0px 0.5em 2em',\n backgroundColor: '#f0f0f0',\n borderRadius: '4px',\n marginTop: '0.5em',\n },\n inlineCode: {\n color: '#700',\n margin: '0px 0.5em 0px 0.5em',\n }\n};\n\nconst levelBadges = [\n <Badge bg=\"success\" style={styles.badge}>OK</Badge>,\n <Badge bg=\"warning\" style={styles.badge}>Warn</Badge>,\n <Badge bg=\"danger\" style={styles.badge}>Error</Badge>,\n <Badge bg=\"secondary\" style={styles.badge}>Stale</Badge>,\n];\n\n/* The right badge for the level */\nexport const LevelBadge = ({level}) => levelBadges[level] || <span>{level}</span>;\n\n/** Reusable component for showing code */\nexport const Code = ({children}) => <pre style={styles.code}>\n {children}\n</pre>;\n\nexport const InlineCode = ({children}) => <tt style={styles.inlineCode}>\n {children}\n</tt>;\n\n\nconst intervals = {};\n\nexport const TimerContext = React.createContext({});\nexport const Timer = ({duration, onTimeout, onStart, setOnDisconnect, children}) => {\n duration = duration || 60;\n const [timer, setTimer] = useState(duration);\n const [running, setRunning] = useState(false);\n const id = useMemo(() => Math.random().toString(36).slice(2), []);\n\n const stop = () => {\n console.log('stopping timer for', id);\n onTimeout && setTimeout(onTimeout, 1);\n clearInterval(intervals[id]);\n intervals[id] = null;\n setRunning(false);\n };\n\n const startTimer = () => {\n const interval = intervals[id];\n console.log(interval, intervals, timer);\n if (!interval && timer > 0) {\n setRunning(true);\n intervals[id] = setInterval(() =>\n setTimer(t => {\n if (--t > 0) {\n return t;\n } else {\n stop();\n }\n }), 1000);\n onStart && setTimeout(onStart, 1);\n }\n\n return stop;\n };\n\n useEffect(() => { timer > 0 && !running && startTimer() }, [timer]);\n\n useEffect(() => stop, []);\n\n setOnDisconnect && setOnDisconnect(() => {\n // call on disconnect of the web component\n stop()\n });\n\n const reset = () => setTimer(duration);\n\n return <TimerContext.Provider value={{reset, duration, timer}}>\n {timer > 0 ? <div>\n {children}\n {timer < 60 && <div>Timeout in: {timer} seconds</div>}\n </div> :\n <div>Timed out. <Button onClick={reset}>\n Resume\n </Button>\n </div>}\n </TimerContext.Provider>;\n};\n\n\n/** Dynamically load and use the Transitive web component specified in the JWT.\n* Embedding Transitive components this way also enables the use of functional\n* and object properties, which get lost when using the custom element (Web\n* Component) because HTML attributes are strings.\n* Example:\n* ```jsx\n* <TransitiveCapability jwt={jwt}\n* myconfig={{a: 1, b: 2}}\n* onData={(data) => setData(data)}\n* onclick={() => { console.log('custom click handler'); }}\n* />\n* ```\n*\n* Always loads the capability specified in the JWT and will default to the\n* main component for that JWT (`-device` or `-fleet`). To specify a secondary\n* component offered by the capability specify `component`, e.g., to load\n* `webrtc-video-supervisor` instead of `webrtc-video-device`, provide a device\n* JWT for webrtc-video and use:\n* ```jsx\n* <TransitiveCapability jwt={jwt}\n* component='webrtc-video-supervisor'\n* auto=\"true\"\n* />\n* ```\n*/\nexport const TransitiveCapability = ({\n jwt, host = 'transitiverobotics.com', ssl = true, ...config\n }) => {\n\n const assertPresent = (value, name) => {\n if (!value) throw new Error(`JWT is missing ${name}`);\n };\n\n const {id, device, capability} = decodeJWT(jwt);\n // Throw an error when any of the above payload is missing\n assertPresent(id, 'id');\n assertPresent(device, 'device');\n assertPresent(capability, 'capability');\n\n const type = device == '_fleet' ? 'fleet' : 'device';\n const capName = capability.split('/')[1];\n const name = `${capName}-${type}`;\n const component = config.component || name;\n\n const { loaded } = useCapability({\n capability,\n name,\n userId: id || config.userId, // accept both id and userId, see #492\n deviceId: device,\n host,\n ssl\n });\n\n const ref = useRef();\n // Attach functional and object properties to the component when ready and\n // on change\n useEffect(() => {\n ref.current?.instance?.setState(s =>\n ({ ...s, id, jwt, host, ssl, ...config }));\n }, [ref.current, loaded, id, jwt, host, ssl, ...Object.values(config)]);\n\n // Disrupt the reactive chain of the MutationObserver to the customElement,\n // so we are not competing with it for updating the props.\n const propClone = useMemo(() => ({id, jwt, host, ssl, ...config}), []);\n\n if (!loaded) return <div>Loading {name}</div>;\n return React.createElement(component, {...propClone, ref});\n };\n\n\n/** A simple error boundary. Usage:\n* ```jsx\n* <ErrorBoundary message=\"Something went wrong\">\n* <SomeFlakyComponent />\n* </ErrorBoundary>\n* ```\n*/\nexport class ErrorBoundary extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n hasError: false,\n messages: [],\n };\n }\n\n static getDerivedStateFromError(error) {\n return { hasError: true };\n }\n\n componentDidCatch(error, errorInfo) {\n console.warn('ErrorBoundary caught:', error, errorInfo);\n this.setState(({messages}) => ({messages: [...messages, error.message]}));\n }\n\n render() {\n return (this.state.hasError ? <div>\n Error: {this.props.message || this.state.messages?.join(', ')\n || 'Something went wrong here.'}\n </div>\n : this.props.children);\n }\n};\n\n\nexport const CapabilityContext = React.createContext({});\n\n/* Only used internally: the actual context provider, given the loaded module */\nconst LoadedCapabilityContextProvider = (props) => {\n const {children, jwt, id, host, ssl, loadedModule} = props;\n\n const context = loadedModule.provideContext?.({\n jwt, id, host, ssl, appReact: React\n });\n\n return <CapabilityContext.Provider value={{ ...context }}>\n {children}\n </CapabilityContext.Provider>;\n};\n\n/**\n* Context provider for capabilities. Use this to access the front-end API\n* provided by some capabilities. Example:\n* ```jsx\n* <CapabilityContextProvider jwt={jwt}>\n* <MyROSComponent />\n* </CapabilityContextProvider>\n* ```\n* where `jwt` is a JWT for a capability that exposes a front-end API. Then use\n* `useContext` in `MyROSComponent` to get the exposed data and functions, e.g.:\n* ```jsx\n* const MyROSComponent = () => {\n* const { ready, subscribe, data } = useContext(CapabilityContext);\n* // When ready, subscribe to the `/odom` topic in ROS1\n* useEffect(() => { ready && subscribe(1, '/odom'); }, [ready]);\n* return <pre>{JSON.stringify(data, true, 2)}</pre>;\n* }\n* ```\n* Where `ready`, `subscribe`, and `data` are reactive variables and functions\n* exposed by the capability of the provided JWT. In this example, the latest\n* message from the subscribed ROS topics will be available in the capabilities\n* namespace in `data`.\n* @param {object} props\n*/\nexport const CapabilityContextProvider =\n ({children, jwt, host = undefined, ssl = undefined}) => {\n\n const {id, device, capability} = decodeJWT(jwt);\n const type = device == '_fleet' ? 'fleet' : 'device';\n const capName = capability.split('/')[1];\n const name = `${capName}-${type}`;\n\n const {loaded, loadedModule} = useCapability({\n capability,\n name,\n userId: id,\n deviceId: device,\n appReact: React,\n host,\n ssl\n });\n\n if (!loadedModule) return <div>Loading {capability}</div>;\n return <LoadedCapabilityContextProvider {...{jwt, id, host, ssl, loadedModule}}>\n {children}\n </LoadedCapabilityContextProvider>;\n };\n\n\n/* whether or not the given react component allows refs, i.e., is either\n * a functional component wrapped with forwardRef or a class component */\nconst componentPermitsRefs = (Component) =>\n (Component.$$typeof == Symbol.for('react.forward_ref'))\n || Component.prototype?.render;\n\n\n/** Create a WebComponent from the given react component and name that is\n* reactive to all attributes. Used in web capabilities. Example:\n* ```js\n* createWebComponent(Diagnostics, 'health-monitoring-device', TR_PKG_VERSION);\n* ```\n*/\nexport const createWebComponent = (Component, name, version = '0.0.0',\n options = {}) => {\n\n // Only create a ref if the component accepts it. This avoids an ugly\n // error in the console when trying to give a ref to a non-forwardRef-wrapped\n // functional component.\n const compRef = componentPermitsRefs(Component) ? React.createRef() : null;\n\n class Wrapper extends React.Component {\n\n onDisconnect = null;\n state = {};\n\n componentDidMount() {\n this.props._element.instance = this;\n this.webComponentConstructed(this.props._element);\n this.props._element.callLifeCycleHook('connectedCallback');\n }\n\n /* function used by `Component` to register a onDisconnect handler */\n setOnDisconnect(fn) {\n this.onDisconnect = fn;\n }\n\n webComponentConstructed(instance) {\n // Observe all changes to attributes and update React state from it\n const observer = new MutationObserver((mutationRecords) => {\n const update = {};\n mutationRecords.forEach(({attributeName}) => {\n update[attributeName] = instance.getAttribute(attributeName);\n });\n this.setState(old => ({...old, ...update}));\n }).observe(instance, { attributes: true });\n }\n\n webComponentDisconnected() {\n // This ensures that the react component unmounts and all useEffect\n // cleanups are called.\n this.setState({_disconnected: true});\n try {\n this.onDisconnect && this.onDisconnect();\n } catch (e) {\n console.log('Error during onDisconnect of web-component', e);\n }\n }\n\n /* method exposed to the wrapped component via prop that allows setting\n * the \"config\" state variable inside the wrapper (not the component\n * itself). This config is retrieved by the portal for inclusion in the\n * embedding instructions. */\n setConfig(config) {\n this.setState({config});\n }\n\n render() {\n const stylesheets = options.stylesheets || [\n // 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css'\n // Bootstrap 5.3.2 css scoped to `.transitive-bs-root`:\n 'https://cdn.jsdelivr.net/gh/transitiverobotics/transitive-utils@0.8.3/web/css/bootstrap_transitive-bs-root.min.css'\n ];\n\n return <div id={`cap-${name}-${version}`}\n className={options.className || 'transitive-bs-root'}>\n <style>\n {stylesheets.map(url => `@import url(${url});`)}\n </style>\n\n {!this.state._disconnected &&\n <Component ref={compRef}\n {...this.props}\n // Important to keep state *after* props for reactivity to work\n {...this.state}\n setOnDisconnect={this.setOnDisconnect.bind(this)}\n setConfig={this.setConfig.bind(this)}\n />}\n </div>;\n }\n };\n\n return ReactWebComponent.create(Wrapper, name, options.shadowDOM || false,\n compRef);\n };\n", "// It is OK to use paths outside of this package because webpack will bundle them\nexport * from '../../common/common.js';\nexport * from '../../common/datacache';\nimport MS from '../../common/MqttSync.js';\nexport const MqttSync = MS;\n\n// moved to common\n// export const decodeJWT = (jwt) => JSON.parse(atob(jwt.split('.')[1]));\n\n/** parse document cookies */\nexport const parseCookie = str =>\n str.split(';')\n .map(v => v.split('='))\n .reduce((acc, v) => {\n acc[decodeURIComponent(v[0].trim())] =\n v[1] && decodeURIComponent(v[1].trim());\n return acc;\n }, {});\n\n/** get or post (if body given) json */\nexport const fetchJson = (url, callback, options = {}) => {\n fetch(url, {\n method: options.method || (options.body ? 'post' : 'get'),\n mode: 'cors',\n cache: 'no-cache',\n // Maybe we'll need this (when embedding)?\n // credentials: 'same-origin', // include, *same-origin, omit\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers\n },\n redirect: 'follow',\n referrerPolicy: 'no-referrer',\n body: options.body ? JSON.stringify(options.body) : undefined\n }).then(res => {\n const error = !res.ok &&\n `fetching ${url} failed: ${res.status} ${res.statusText}`;\n res.json()\n .then(data => callback(error, data))\n .catch(err => {\n throw new Error(err);\n });\n }).catch((error) => callback(`error: ${error}`));\n};\n", "import React, { useState, useEffect, useMemo } from 'react';\nimport _ from 'lodash';\n// import mqtt1 from 'mqtt';\nimport mqtt from 'mqtt/dist/mqtt.esm';\n\nimport { decodeJWT, getLogger, clone, pathToTopic, mergeVersions, topicToPath }\n from './client';\nconst MqttSync = require('../../common/MqttSync');\n\nconst log = getLogger('utils-web/hooks');\nlog.setLevel('debug');\n\nconst RECONNECT_PERIOD_DEFAULT = 1000; // default time until mqtt retries connecting\nconst RECONNECT_PERIOD_MAX = 20000; // max retry time (after dynamic backoff)\n\n/** Hook for using MqttSync in React.\n* @returns {object} An object `{data, mqttSync, ready, StatusComponent, status}`\n* where:\n* `data` is a reactive data source in React containing all the data received by\n* mqttsync,\n* `mqttSync` is the MqttSync object itself,\n* `ready` indicates when mqttSync is ready to be used (connected and received\n* successfully subscribed to mqtt system heartbeats)\n*/\nexport const useMqttSync = ({jwt, id, mqttUrl, appReact}) => {\n const { useState, useRef, useEffect } = appReact || React;\n\n const [status, setStatus] = useState('connecting');\n const [mqttSync, setMqttSync] = useState();\n const [data, setData] = useState({});\n // True once the subscription to the system heartbeat has been granted.\n const [heartbeatGranted, setHeartbeatGranted] = useState(false);\n\n useEffect(() => {\n const payload = decodeJWT(jwt);\n\n // pre-check validity of JWT, don't use if expired\n const { validity, iat } = payload;\n if (!validity || !iat || (iat + validity) * 1e3 < Date.now()) {\n const error = 'The provided JWT is invalid or expired.';\n log.warn(error, payload);\n setStatus(`error: ${error}`);\n return;\n }\n\n /** Implement dynamic backoff when we fail to connect. */\n let reconnectPeriod = RECONNECT_PERIOD_DEFAULT; // default to start with\n const transformWsUrl = (url, options, client) => {\n options.reconnectPeriod = reconnectPeriod;\n return url;\n }\n\n log.debug('(re-)create mqtt client');\n const client = mqtt.connect(mqttUrl, {\n username: JSON.stringify({id, payload}),\n password: jwt,\n transformWsUrl\n });\n\n client.on('reconnect', () => {\n log.debug('reconnect');\n });\n\n // Increase backoff with each close (since we can't actually detect auth\n // errors); stop reconnecting if JWT is invalid by now.\n client.on('close', () => {\n log.debug('close');\n reconnectPeriod = Math.min(reconnectPeriod * 2, RECONNECT_PERIOD_MAX);\n\n if ((iat + validity) * 1e3 < Date.now()) {\n const error = 'MQTT connection closed and the JWT is expired by now. Not reconnecting.';\n log.warn(error, payload);\n setStatus(`error: ${error}`);\n // give up, do not try to reconnect; user needs to provide a fresh JWT.\n client.end();\n } else {\n const msg = `reconnect in ${reconnectPeriod / 1000} s`;\n log.info(msg);\n setStatus(msg);\n }\n });\n\n // reset to default after a successful connection\n client.on('connect', () => {\n reconnectPeriod = RECONNECT_PERIOD_DEFAULT;\n log.debug('MQTT (re-)connected');\n setStatus('connected');\n });\n\n client.once('connect', () => {\n const mqttSyncClient = new MqttSync({\n mqttClient: client,\n ignoreRetain: true,\n onHeartbeatGranted: () => setHeartbeatGranted(true)\n });\n setMqttSync(mqttSyncClient);\n\n // Update data on change. Note: need to clone object to force reaction\n mqttSyncClient.data.subscribe(_.throttle(() =>\n setData(clone(mqttSyncClient.data.get())), 50));\n });\n\n client.on('error', (error) => {\n log.error(error);\n setStatus(`error: ${error}`);\n });\n\n return () => {\n log.info('cleaning up useMQTTSync');\n if (mqttSync && mqttSync.beforeDisconnect) {\n mqttSync.beforeDisconnect();\n mqttSync.waitForHeartbeatOnce(() => client.end());\n } else {\n client.end();\n }\n };\n }, [jwt, id]);\n\n return {\n status,\n // ready: status == 'connected',\n ready: heartbeatGranted,\n StatusComponent: () => <div>{status}</div>,\n mqttSync, // Note: mqttSync.data is not reactive.\n data, // This is a reactive data-source (to use meteor terminology).\n };\n};\n\n/** Hook for using Transitive in React. Connects to MQTT, establishes sync, and\n* exposes reactive `data` state variable. */\nexport const useTransitive =\n ({jwt, id, capability, versionNS, appReact,\n host = 'transitiverobotics.com', ssl = true }) => {\n\n const [scope, capabilityName] = capability.split('/');\n\n const { device } = decodeJWT(jwt);\n const prefixPath = [id, device, scope, capabilityName];\n const prefix = pathToTopic(prefixPath);\n const prefixPathVersion = [...prefixPath, versionNS];\n const prefixVersion = pathToTopic(prefixPathVersion);\n\n const mqttUrl = `${ssl && JSON.parse(ssl) ? 'wss' : 'ws'}://mqtt.${host}`;\n const fromMqttSync = useMqttSync({ jwt, id, mqttUrl, appReact });\n\n return {...fromMqttSync, device, prefixPath, prefix, prefixPathVersion,\n prefixVersion};\n };\n\n\n/** Subscribe to MqttSync topics using the provided JWT. This will\n* automatically find which version of the capability named in the JWT is running\n* on the device of the JWT and get the data for that version.\n*\n* Example usage (with webrtc-video):\n*\n* ```js\n* const { agentStatus, topicData } = useTopics({ jwt, topics: [\n* '/options/videoSource',\n* '/stats/+/log/'\n* ]});\n* ```\n*\n* @param {object} options An object containing:\n* `JWT`: A list of subtopics of the capability named in the JWT.\n* `topics`: A list of subtopics of the capability named in the JWT.\n* @returns {object} An object `{data, mqttSync, ready, agentStatus, topicData}`\n* where:\n* `agentStatus` is the `status` field of the running robot agent, including\n* heartbeat and runningPackages, and\n* `topicData` is the data for the selected topics of the capability\n*/\nexport const useTopics = ({jwt, host = 'transitiverobotics.com', ssl = true,\n topics = [], appReact}) => {\n\n const { useState, useEffect } = appReact || React;\n\n // We need to make sure we don't resubscribe (below) when this function\n // is called with the same content of `topics` but a different object.\n const [topicList, setTopicList] = useState();\n !_.isEqual(topicList, topics) && setTopicList(topics);\n\n const {device, id, capability} = decodeJWT(jwt);\n if (device == '_fleet') {\n log.warn('useTopics only works for device JWTs, not _fleet ones');\n return;\n }\n\n const agentPrefix = `/${id}/${device}/@transitive-robotics/_robot-agent/+/status`;\n\n const {mqttSync, data, status, ready, StatusComponent} =\n useMqttSync({jwt, id, mqttUrl: `ws${ssl ? 's' : ''}://mqtt.${host}`, appReact});\n\n useEffect(() => {\n if (ready) {\n mqttSync.subscribe(agentPrefix, (err) => err && console.warn(err));\n }\n }, [mqttSync, ready]);\n\n const agentStatus = mergeVersions(\n data[id]?.[device]['@transitive-robotics']['_robot-agent'], 'status').status;\n const runningPackages = agentStatus?.runningPackages;\n\n const [scope, capName] = capability.split('/');\n const versions = runningPackages?.[scope]?.[capName];\n const runningVersion = versions && Object.values(versions).filter(Boolean)[0];\n const prefix = `/${id}/${device}/${capability}/${runningVersion}`;\n\n useEffect(() => {\n log.debug('topics', topics);\n if (runningVersion) {\n topics.forEach(topic => {\n log.debug(`subscribing to ${prefix}${topic}`);\n mqttSync.subscribe(`${prefix}${topic}`,\n (err) => err && log.warn(err));\n });\n }\n }, [topicList, runningVersion, mqttSync]);\n\n const topicData = _.get(data, topicToPath(prefix));\n // log.debug(data, agentStatus, topicData);\n\n return {data: data?.[id]?.[device], mqttSync, agentStatus, topicData};\n };\n\n\nconst listeners = {};\nconst loadedModules = {};\n/** Hook to load a Transitive capability. Besides loading the custom element,\n* this hook also returns any functions and objects the component exports in\n* `loadedModule`. Example:\n* ```js\n* const {loaded, loadedModule} = useCapability({\n* capability: '@transitive-robotics/terminal',\n* name: 'mock-device',\n* userId: 'user123',\n* deviceId: 'd_mydevice123',\n* });\n* ```\n*/\nexport const useCapability = ({ capability, name, userId, deviceId,\n host = 'transitiverobotics.com', ssl = true, appReact\n }) => {\n const { useState, useEffect } = appReact || React;\n\n const [returns, setReturns] = useState({ loaded: false });\n\n // called when loaded\n const done = (message, theModule) => {\n log.debug(`custom component ${name}: ${message}`);\n loadedModules[name] = theModule;\n setReturns(x => ({...x, loadedModule: theModule, loaded: !!theModule}));\n };\n\n /** set the returns for all listeners */\n const notifyListeners = (...args) => listeners[name].forEach(l => l(...args));\n\n useEffect(() => {\n log.debug(`loading custom component ${name}`);\n\n if (loadedModules[name]) {\n return done('already loaded', loadedModules[name]);\n }\n if (listeners[name]) {\n log.debug('already loading');\n // get notified when loading completes\n listeners[name].push(done);\n return;\n }\n listeners[name] = [done];\n\n const baseUrl = `http${ssl ? 's' : ''}://portal.${host}`;\n const params = new URLSearchParams({ userId, deviceId });\n // filename without extension as we'll try multiple\n const fileBasename = `${baseUrl}/running/${capability}/dist/${name}`;\n\n /* Since some users use webpack and webpack is stupid, we need to use\n this magic comment for it to ignore these (remote) requests, see:\n https://webpack.js.org/api/module-methods/#webpackignore. */\n import(/* webpackIgnore: true */\n `${fileBasename}.esm.js?${params.toString()}`).then(\n esm => notifyListeners('loaded esm', esm),\n error => {\n log.warn(`No ESM module found for ${name}, loading iife`, error);\n import(/* webpackIgnore: true */\n `${fileBasename}.js?${params.toString()}`).then(\n iife => notifyListeners('loaded iife', iife),\n error => log.error(`Failed to load ${name} iife`, error));\n });\n }, [capability, name, userId, deviceId]);\n\n return returns;\n };\n\n\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/*\n * An optional library which is conditionally added\n * @returns {[]}\n */\nmodule.exports = () => {\n try {\n return require('react-web-component-style-loader/exports').styleElements;\n } catch (e) {\n return [];\n }\n};\n", "/*\n * Takes in a node attributes map and returns an object with camelCased\n * properties and values\n * @param nodeMap\n * @returns {{}}\n */\nmodule.exports = function extractAttributes(nodeMap) {\n if (!nodeMap.attributes) {\n return {};\n }\n\n let obj = {};\n let attribute;\n const attributesAsNodeMap = [...nodeMap.attributes];\n const attributes = attributesAsNodeMap.map((attribute) =>\n ({ [attribute.name]: attribute.value }));\n\n for (attribute of attributes) {\n const key = Object.keys(attribute)[0];\n const camelCasedKey = key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());\n obj[camelCasedKey] = attribute[key];\n }\n\n return obj;\n};\n", "const React = require('react');\nconst ReactDOM = require('react-dom');\nconst { createRoot } = require('react-dom/client');\n\n// const { createRoot } = require('react-dom/client'); // react 18; wip\nconst retargetEvents = require('react-shadow-dom-retarget-events');\nconst getStyleElementsFromReactWebComponentStyleLoader = require('./getStyleElementsFromReactWebComponentStyleLoader');\nconst extractAttributes = require('./extractAttributes');\n\n// require('@webcomponents/shadydom');\n// require('@webcomponents/custom-elements');\n\nconst lifeCycleHooks = {\n attachedCallback: 'webComponentAttached',\n connectedCallback: 'webComponentConnected',\n disconnectedCallback: 'webComponentDisconnected',\n adoptedCallback: 'webComponentAdopted'\n};\n\nmodule.exports = {\n /*\n * @param {JSX.Element} wrapper: the wrapper component class to be instantiated and wrapped\n * @param {string} tagName - The name of the web component. Has to be minus \"-\" delimited.\n * @param {boolean} useShadowDom - If the value is set to \"true\" the web component will use the `shadowDom`. The default value is true.\n */\n create: (wrapper, tagName, useShadowDom = true, compRef = undefined) => {\n\n const proto = class extends HTMLElement {\n instance = null; // the instance we create of the wrapper\n\n callConstructorHook() {\n if (this.instance['webComponentConstructed']) {\n this.instance['webComponentConstructed'].apply(this.instance, [this])\n }\n }\n\n callLifeCycleHook(hook, params = []) {\n const method = lifeCycleHooks[hook];\n if (method && this.instance && this.instance[method]) {\n this.instance[method].apply(this.instance, params);\n }\n }\n\n connectedCallback() {\n const self = this;\n let mountPoint = self;\n\n if (useShadowDom) {\n // Re-assign the self (this) to the newly created shadowRoot\n const shadowRoot = self.attachShadow({ mode: 'open' });\n\n // Re-assign the mountPoint to the newly created \"div\" element\n mountPoint = document.createElement('div');\n\n // Move all of the styles assigned to the react component inside of\n // the shadowRoot. By default this is not used, only if the library is\n // explicitly installed\n const styles = getStyleElementsFromReactWebComponentStyleLoader();\n styles.forEach((style) => {\n shadowRoot.appendChild(style.cloneNode(shadowRoot));\n });\n\n shadowRoot.appendChild(mountPoint);\n retargetEvents(shadowRoot);\n }\n\n createRoot(mountPoint).render(\n // This is where we instantiate the actual component (in its wrapper)\n React.createElement(wrapper, {_element: self, ...extractAttributes(self)})\n );\n }\n\n disconnectedCallback() {\n this.callLifeCycleHook('disconnectedCallback');\n }\n\n adoptedCallback(oldDocument, newDocument) {\n this.callLifeCycleHook('adoptedCallback', [oldDocument, newDocument]);\n }\n\n /* call a function defined in the component, either as a class method, or\n * via useImperativeHandle */\n call(functionName, args) {\n return compRef?.current?.[functionName]?.call(compRef?.current, args);\n }\n\n /* predefined function to retrieve the pre-defined config object of the\n * state, populated via the pre-defined `setConfig` method given as prop\n * to the wrapped component. */\n getConfig() {\n return this.instance.state.config;\n }\n }\n\n customElements.define(tagName, proto);\n\n return proto;\n },\n};\n", "\nconst _ = {\n get: require('lodash/get'),\n set: require('lodash/set'),\n forEach: require('lodash/forEach'),\n map: require('lodash/map'),\n isPlainObject: require('lodash/isPlainObject'),\n};\n\n\n// -------------------------------------------------------------------------\n// DataCache tools\n\n/** convert topic to path array */\nconst topicToPath = (topic) => {\n // split topic by slashes, but not if they are escaped\n // const path = topic.match(/(\\\\.|[^/])+/g) || [];\n // split topic by slashes and unescape slashes in each item\n const path = topic.split('/').map(decodeTopicElement);\n // handle leading slash\n path.length > 0 && path[0].length == 0 && path.shift();\n // handle trailing slash\n path.length > 0 && path.at(-1).length == 0 && path.pop();\n return path;\n};\n\n\n/** convert a path array to mqtt topic; reduces +id identifiers to + */\nconst pathToTopic = (pathArray) => {\n /** reduce wildcards with Ids, such as `+sessionId`, to just + */\n const dropWildcardIds = (x) => x.startsWith('+') ? '+' : x;\n return `/${pathArray.map(dropWildcardIds).map(encodeTopicElement).join('/')}`;\n};\n\n/**\n * Given an object, return a new flat object of topic+value pairs, e.g.:\n```js\n{a: {b: 1, c: 2}, d: 3} \u2192 {'/a/b': 1, '/a/c': 2, '/d': 3}\n```\nNote: not idempotent!\n```js\n{'/a/b': 1, '/a/c': 2, d: 3} \u2192 {'%2Fa%2Fb': 1, '%2Fa%2Fc': 2, '/d': 3}\n```\n*/\nconst toFlatObject = (obj, prefix = [], rtv = {}) => {\n _.forEach(obj, (value, key) => {\n // const newPrefix = prefix.concat(topicToPath(String(key)));\n const newPrefix = prefix.concat(String(key));\n\n // TODO: using isPlainObject also means custom objects (classes) do not get\n // broken down.\n if ((_.isPlainObject(value) || value instanceof Array) && value !== null) {\n // it's an object or array\n toFlatObject(value, newPrefix, rtv);\n } else {\n // it's a primitive\n rtv[pathToTopic(newPrefix)] = value;\n }\n });\n return rtv;\n};\n\n/** Iterate through the object and invoke callback for each match of path (with\nnamed wildcards) */\nconst forMatchIterator = (obj, path, callback, pathSoFar = [], matchSoFar = {}) => {\n\n if (path.length == 0 || path[0] == '#') {\n callback(obj, pathSoFar, matchSoFar);\n return;\n }\n\n const next = path[0]; // don't use shift, we don't want to modify path\n if (next) {\n for (let key in obj) {\n if (key == next || next == '*' || next.startsWith('+')) {\n const match = next.startsWith('+') && next.length > 1 ?\n Object.assign({}, matchSoFar, {[next.slice(1)]: key}) :\n matchSoFar;\n forMatchIterator(obj[key], path.slice(1), callback,\n pathSoFar.concat([key]), match);\n }\n }\n }\n};\n\n/** Like _.set but without arrays. This allows using numbers as keys. */\nconst setFromPath = (obj, path, value) => {\n if (path.length == 0) return obj;\n const next = path.shift();\n if (path.length == 0) {\n obj[next] = value;\n } else {\n if (!obj[next]) obj[next] = {};\n setFromPath(obj[next], path, value);\n }\n};\n\nconst encodeTopicElement = x => x.replace(/%/g, '%25').replace(/\\//g, '%2F');\nconst decodeTopicElement = x => x.replace(/%25/g, '%').replace(/%2F/g, '/');\n\n\n/** Match a slash-separated topic or path array with a selector using +XYZ for\n* (named) wildcards. Return the matching result.\n*/\nconst topicMatch = (selector, topic) => {\n const byArray = (s, p) => {\n if (s.length == 0) return true; // we are done: prefix matched\n if (s[0][0] == '#') return true; // explicit tail-wildcard\n // if (p.length < s.length) return false;\n if (p.length == 0) return true; // we are done, matched all\n // simple match:\n if (s[0] == p[0]) return byArray(s.slice(1), p.slice(1));\n // wild card match:\n if (s[0][0] == '+') {\n const sub = byArray(s.slice(1), p.slice(1));\n return sub && Object.assign({[s[0].slice(1)]: p[0]}, sub);\n }\n // else: failure!\n return false;\n };\n\n const selectorArray = Array.isArray(selector) ? selector : topicToPath(selector);\n const pathArray = Array.isArray(topic) ? topic : topicToPath(topic);\n return byArray(selectorArray, pathArray);\n};\n\n/** sub is a strict sub-topic of parent, and in particular not equal */\nconst isSubTopicOf = (sub, parent) => {\n const pPath = topicToPath(parent);\n const sPath = topicToPath(sub);\n return isPrefixOf(pPath, sPath) && pPath.length < sPath.length;\n};\n\n/** prefixArray is a prefix of the array */\nconst isPrefixOf = (prefixArray, array) => {\n if (prefixArray.length == 0) return true;\n return (prefixArray[0] == array[0] &&\n isPrefixOf(prefixArray.slice(1), array.slice(1))\n );\n}\n\n\nmodule.exports = {\n topicToPath,\n pathToTopic,\n toFlatObject,\n forMatchIterator,\n setFromPath,\n encodeTopicElement,\n decodeTopicElement,\n topicMatch,\n isSubTopicOf,\n};", "module.exports = {\n rosReleases: {\n kinetic: { rosVersion: 1, ubuntuCodename: 'xenial' },\n melodic: { rosVersion: 1, ubuntuCodename: 'bionic' },\n noetic: { rosVersion: 1, ubuntuCodename: 'focal' },\n dashing: { rosVersion: 2 },\n eloquent: { rosVersion: 2 },\n foxy: { rosVersion: 2 },\n galactic: { rosVersion: 2 },\n humble: { rosVersion: 2 },\n iron: { rosVersion: 2 },\n jazzy: { rosVersion: 2 },\n kilted: { rosVersion: 2 },\n rolling: { rosVersion: 2 },\n }\n};\n", "const semverCompare = require('semver/functions/compare');\nconst semverMinVersion = require('semver/ranges/min-version');\n\nconst _ = {\n get: require('lodash/get'),\n set: require('lodash/set'),\n unset: require('lodash/unset'),\n forEach: require('lodash/forEach'),\n map: require('lodash/map'),\n isEmpty: require('lodash/isEmpty'),\n eq: require('lodash/isEqual'),\n isPlainObject: require('lodash/isPlainObject'),\n merge: require('lodash/merge'),\n};\n\nconst loglevel = require('loglevel');\nconst prefix = require('loglevel-plugin-prefix');\nconst chalk = require('chalk');\n\nconst { topicToPath, pathToTopic, toFlatObject, setFromPath, forMatchIterator,\n topicMatch, isSubTopicOf, encodeTopicElement, decodeTopicElement }\n = require('./datacache/tools');\n\nconst constants = require('./constants');\n\n// ----------------------------------------------------------------------------\n// Logging, incl. logger prefix\n\n/* Convenience function to set all loggers to the given level. */\nloglevel.setAll = (level) =>\n Object.values(loglevel.getLoggers()).forEach(l => l.setLevel(level));\n\nconst logColors = {\n warn: chalk.yellow,\n error: chalk.red,\n info: chalk.green,\n debug: chalk.gray,\n};\n\nconst levelFormatter =\n (level) => logColors[level] ? logColors[level](level) : level;\n\nprefix.reg(loglevel);\n\nif (typeof window != 'undefined') {\n // browser: keep it simple\n prefix.apply(loglevel, {\n template: '[%n %l]',\n });\n} else {\n // back-end + robot: include timestamp and use colors\n prefix.apply(loglevel, {\n template: '[%t %n %l]',\n levelFormatter,\n timestampFormatter: date => chalk.blue(date.toISOString()),\n });\n}\n\n/** Get a new loglevel logger; call with a name, e.g., `module.id`. The returned\n* logger has methods trace, debug, info, warn, error. See\n* https://www.npmjs.com/package/loglevel for details.\n*/\nconst getLogger = loglevel.getLogger;\n\n// ----------------------------------------------------------------------------\n\n/** Deep-clone the given object. All functionality is lost, just data is kept. */\nconst clone = (obj) => JSON.parse(JSON.stringify(obj));\n\n/** Parse JWT and return the decoded payload (JSON). */\nconst decodeJWT = (jwt) => JSON.parse(atob(jwt.split('.')[1]));\n// TODO: make this robust against bad JWTs (throw a more readable error)\n\n/** Try parsing JSON, return null if unsuccessful */\nconst tryJSONParse = (string) => {\n try {\n return JSON.parse(string);\n } catch (e) {\n return null;\n }\n};\n\n/** Reusable visitor pattern: iteratively visits all nodes in the tree\n described by `object`, where `childField` indicates the child-of predicate.\n*/\nconst visit = (object, childField, visitor) => {\n if (!object) return;\n visitor(object);\n object[childField]?.forEach(child => visit(child, childField, visitor));\n};\n\n/** Given an object and a path, visit each ancestor of the path */\nconst visitAncestor = (object, path, visitor, prefix = []) => {\n visitor(object, prefix);\n const next = path[0];\n if (next) {\n const sub = object[next];\n if (sub) {\n visitAncestor(sub, path.slice(1), visitor, prefix.concat(next));\n }\n }\n};\n\n/** Wait for delay ms, for use in async functions. */\nconst wait = (delay) => new Promise((resolve) => { setTimeout(resolve, delay); });\n\n\n\n\n// -------------------------------------------------------------------------\n// MQTT Tools\n\n/** parse usernames used in MQTT */\nconst parseMQTTUsername = (username) => {\n const parts = username.split(':');\n return {\n organization: parts[0],\n client: parts[1],\n sub: parts.slice(2)\n }\n};\n\n/** parse an MQTT topic according to our topic schema */\nconst parseMQTTTopic = (topic) => {\n const parts = topicToPath(topic);\n return {\n organization: parts[0],\n device: parts[1],\n capabilityScope: parts[2],\n capabilityName: parts[3],\n capability: `${parts[2]}/${parts[3]}`,\n version: parts[4],\n sub: parts.slice(5)\n }\n};\n\nconst mqttParsePayload = (payload) =>\n payload.length == 0 ? null : JSON.parse(payload.toString('utf-8'));\n// TODO: ^ This can probably now just become tryJSONParse\n\n/** delete all retained messages in a certain topic prefix, waiting for\n a given delay to collect existing retained. Use with care, never delete topics\n not owned by us. Harmless within capabilities, which are namespaced already.\n*/\nconst mqttClearRetained = (mqttClient, prefixes, callback, delay = 1000) => {\n\n const toDelete = [];\n const collectToDelete = (topic) => {\n // there may be other mqtt subscriptions running, filter by topic\n prefixes.forEach(prefix =>\n // mqttTopicMatch(topic, `${prefix}/#`) && toDelete.push(topic)\n topicMatch(`${prefix}/#`, topic) && toDelete.push(topic)\n );\n }\n mqttClient.on('message', collectToDelete);\n\n // subscribe to all\n prefixes.forEach(prefix => {\n if (typeof prefix == 'string') {\n mqttClient.subscribe(`${prefix}/#`);\n } else {\n console.warn('Ignoring', prefix, 'since it is not a string.');\n }\n });\n\n // value to use to clear, depending on node.js vs. browser\n const nullValue = (typeof Buffer != 'undefined' ? Buffer.alloc(0) : null);\n\n setTimeout(() => {\n mqttClient.removeListener('message', collectToDelete);\n prefixes.forEach(prefix => mqttClient.unsubscribe(`${prefix}/#`));\n\n const count = toDelete.length;\n console.log(`clearing ${count} retained messages from ${prefixes}`);\n toDelete.forEach(topic => {\n mqttClient.publish(topic, nullValue, {retain: true});\n });\n\n callback && callback(count);\n }, delay);\n};\n\n\n// WIP: a cleaner, more explicit way to serialize/deserialize mqtt payloads\n// /** Serialize a message for transport via MQTT */\n// const mqttSerialize = (message) => {\n// return value == null ? null : JSON.stringify(message);\n// };\n\n// /** Deserialize a message from MQTT */\n// const mqttDeserialize = (payload) => {\n// return payload.length == 0 ? null : JSON.parse(payload.toString('utf-8'));\n// };\n\n\n// -------------------------------------------------------------------------\n\n/** Generate a random id (base36) */\nconst getRandomId = (bytes = 6) => {\n const buffer = new Uint8Array(bytes);\n crypto.getRandomValues(buffer);\n return buffer.reduce((memo, i) => memo + i.toString(36), '');\n};\n\n/** Convert number to base52 [a-zA-Z] */\nconst toBase52 = (num) => {\n const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n const list = [];\n do {\n list.unshift(characters[num % 52]);\n num = Math.floor(num / 52);\n } while (num > 0);\n return list.join('');\n}\n\n/** Get a base52 representation [a-zA-Z] of the current date (ms since epoch) */\nconst getDateBase52 = () => toBase52(Date.now());\n\n// -------------------------------------------------------------------------\n\n/** Compare to version strings. Return -1 if a is lower than b,\n0 if they are equal, and 1 otherwise. If either is not a complete version,\ne.g., 2.0, interpret it as a range and use its minimum version for the\ncomparison. Hence, 2.0 < 2.0.1. */\nconst versionCompare = (a, b) =>\n semverCompare(semverMinVersion(a), semverMinVersion(b));\n\n// -------------------------------------------------------------------------\n\n/** given an object where the keys are versions, merge this into one object\n where the latest version of each subfield overwrites any previous */\nconst mergeVersions = (versionsObject, subTopic = undefined, options = {}) => {\n if (!versionsObject) {\n return subTopic ? _.set({}, subTopic, versionsObject) : versionsObject;\n }\n\n const versions = Object.keys(versionsObject).filter(ver =>\n (!options.maxVersion || versionCompare(ver, options.maxVersion) <= 0) &&\n (!options.minVersion || versionCompare(options.minVersion, ver) <= 0))\n .sort(versionCompare);\n\n const merged = {};\n const subPath = subTopic && topicToPath(subTopic);\n versions.forEach(nextVersion => {\n const newValue = subPath ? _.get(versionsObject[nextVersion], subPath) :\n versionsObject[nextVersion];\n // Object.assign(merged, newValue);\n _.merge(merged, newValue);\n });\n return subPath ? _.set({}, subPath, merged) : merged;\n};\n\n// -------------------------------------------------------------------------\n// Formatting tools\n\nconst units = ['B', 'KB', 'MB', 'GB', 'TB'];\nconst formatBytes = (bytes) => {\n if (!bytes) return '--';\n let i = 0;\n while (bytes > 1024) {\n bytes /= 1024;\n i++;\n }\n return `${bytes.toFixed(2)} ${units[i]}`;\n}\n\nconst formatDuration = (seconds) => {\n if (!seconds) return '--';\n const parts = {};\n if (seconds > 3600) {\n parts.h = Math.floor(seconds / 3600);\n seconds = seconds % 3600;\n }\n if (seconds > 60) {\n parts.m = Math.floor(seconds / 60);\n seconds = seconds % 60;\n }\n parts.s = Math.floor(seconds);\n\n let rtv = '';\n parts.h > 0 && (rtv += `${parts.h}h `);\n parts.m > 0 && (rtv += `${parts.m}m `);\n !parts.h && (rtv += `${parts.s}s`);\n return rtv.trim();\n};\n\n// -------------------------------------------------------------------------\n\nmodule.exports = { parseMQTTUsername, parseMQTTTopic,\n pathToTopic, topicToPath, toFlatObject, topicMatch,\n mqttParsePayload, getRandomId, toBase52, getDateBase52, versionCompare,\n loglevel, getLogger,\n mergeVersions, mqttClearRetained, isSubTopicOf, clone, setFromPath,\n forMatchIterator, encodeTopicElement, decodeTopicElement, constants, visit,\n wait, formatBytes, formatDuration, tryJSONParse,\n decodeJWT, visitAncestor\n};\n", "\nconst _ = {\n get: require('lodash/get'),\n set: require('lodash/set'),\n unset: require('lodash/unset'),\n forEach: require('lodash/forEach'),\n map: require('lodash/map'),\n isEmpty: require('lodash/isEmpty'),\n eq: require('lodash/isEqual'),\n isPlainObject: require('lodash/isPlainObject'),\n merge: require('lodash/merge'),\n};\n\nconst {topicToPath, pathToTopic, toFlatObject, topicMatch, forMatchIterator}\n = require('./tools');\n\n/** Unset the topic in that obj, and clean up parent if empty, recursively.\nReturn the path to the removed node.\n*/\nconst unset = (obj, path) => {\n if (!path || path.length == 0) return;\n _.unset(obj, path);\n const parentPath = path.slice(0, -1);\n // _.get doesn't do the intuitive thing for the empty path, handle it ourselves\n const parent = parentPath.length == 0 ? obj : _.get(obj, parentPath);\n if (_.isEmpty(parent)) {\n return unset(obj, parentPath);\n } else {\n return path;\n }\n};\n\n/** Given a modifier `{\"a/b/c\": \"xyz\"}` update the object `obj` such that\n`obj.a.b.c = \"xyz\"`. */\nconst updateObject = (obj, modifier) => {\n _.forEach( modifier, (value, topic) => {\n const path = topicToPath(topic);\n if (value == null) {\n unset(obj, path);\n } else {\n _.set(obj, path, value);\n }\n });\n return obj;\n};\n\n/** Given an object and a path with wildcards (`*` and `+`), *modify* the object\nto only contain elements matched by the path, e.g.,\n`{a: {b: 1, c: 2}, d: 2}` and `['a','+']` would give `{a: {b: 1, c: 2}}`\n\n@param {object} obj - The object to select from\n@param {array} path - An array specifying the path to select, potentially\ncontaining mqtt wildcards ('+').\n*/\nconst selectFromObject = (obj, path) => {\n if (path.length == 0) return;\n const next = path[0];\n if (next) {\n for (let key in obj) {\n if (key != next && next != '*' && !next.startsWith('+')) {\n delete obj[key];\n } else {\n selectFromObject(obj[key], path.slice(1));\n }\n }\n }\n};\n\n\n/**\n* A class implementing a local data cache, used as a local data store with\n* deduplication detection and update events. While this class is very handy\n* you probably won't need to create instances of it directly. Instead use\n* the mqttSync.data instance which holds the locally stored data\n* subscribed/published from/to MQTTSync.\n* For example on the robot:\n* ```js\n* // update/publish our status:\n* mqttSync.data.update('status', {changed: Date.now(), msg: 'OK'});\n* // subscribe to new user requests (e.g., from UI):\n* mqttSync.data.subscribePath('+user/request', (request, key, {user}) => {\n* log.debug(`user ${user} made request`, request);\n* });\n* ```\n* In the cloud or in a web component you would need to use the full topic including\n* org, device, scope, cap-name, and version.\n*/\nclass DataCache {\n\n #data = {};\n #listeners = [];\n #flatListeners = [];\n\n constructor(data = {}) {\n this.#data = data;\n\n // for now add an alias\n this.subscribeTopic = this.subscribePath;\n }\n\n /** Update the object with the given value at the given path, remove empty;\n return the flat changes (see toFlatObject). Add `tags` to updates to mark\n them somehow based on the context, e.g., so that some subscriptions can choose\n to ignore updates with a certain tag.\n */\n updateFromArray(path, value, tags = {}) {\n // const empty = Object.keys(this.#data).length == 0; // object already empty\n const current = _.get(this.#data, path);\n if (value == null) {\n if (current === undefined || current === null) {\n return {}; // no change, do not call listeners\n } else {\n unset(this.#data, path);\n }\n } else {\n if (_.eq(current, value)) {\n // note: this is just a shallow equal, so replacing a sub-document\n // with an atomic copy of it should still trigger listeners.\n // TODO: Note also that when value is an object, we will set it by\n // reference here, so any changes made to that object will *not* trigger\n // listeners because `current` will already be changed -- which is\n // probably wrong. May want to always clone value first.\n return {}; // nothing to do, do not bother listeners\n }\n // console.log('setting', path, value);\n _.set(this.#data, path, value);\n // TODO: implement this ourselves so we can do better change-checking\n }\n\n const topic = pathToTopic(path);\n const obj = {[topic]: value};\n\n // flatten the value and combine eith topic (without reflattening the topic):\n let flatChanges;\n if (value instanceof Object) {\n const flatValue = toFlatObject(value);\n flatChanges = {};\n _.forEach(flatValue, (atomic, flatKey) => {\n flatChanges[`${topic}${flatKey}`] = atomic;\n });\n } else {\n flatChanges = obj;\n }\n\n // option 1. using flat changes (sub-documents are never atomic)\n // this.#listeners.forEach(fn => fn(flatChanges));\n\n // option 2. allow atomic sub-document changes\n this.#listeners.forEach(fn => fn(obj, tags));\n\n this.#flatListeners.forEach(fn => fn(flatChanges, tags));\n\n return flatChanges;\n }\n\n /** Update the value at the given path (array or dot separated string) */\n update(path, value, tags) {\n if (typeof path == 'string') {\n return this.updateFromTopic(path, value, tags);\n } else if (path instanceof Array) {\n return this.updateFromArray(path, value, tags);\n } else {\n throw new Error('unrecognized path expression');\n }\n }\n\n /** Set value from the given topic (with or without leading or trailing slash) */\n updateFromTopic(topic, value, tags) {\n return this.updateFromArray(topicToPath(topic), value, tags);\n }\n\n /** Update data from a modifier object where keys are topic names to be\n interpreted as paths, and values are the values to set */\n updateFromModifier(modifier, tags) {\n return _.map(modifier, (value, topic) =>\n this.updateFromTopic(topic, value, tags));\n }\n\n /** Add a callback for all change events. */\n subscribe(callback) {\n if (callback instanceof Function) {\n this.#listeners.push(callback);\n } else {\n console.warn('DataCache.subscribe expects a function as argument. Did you mean to use subscribePath?');\n }\n }\n\n /** Subscribe to a specific path (array) only. Callback receives\n `value, key, matched, tags`. */\n subscribePath(path, callback) {\n this.#listeners.push((changes, tags) => {\n _.forEach(changes, (value, key) => {\n const matched = topicMatch(path, key);\n matched && callback(value, key, matched, tags);\n });\n });\n }\n\n /** Subscribe to a specific topic only. Callback receives\n `value, key, matched, tags`. */\n subscribeTopic(topic, callback) {\n this.subscribePath(topic, callback);\n }\n\n /** Same as subscribePath but always get all changes in flat form */\n subscribePathFlat(topic, callback) {\n this.#flatListeners.push((changes, tags) => {\n _.forEach(changes, (value, key) => {\n const matched = topicMatch(topic, key);\n matched && callback(value, key, matched, tags);\n });\n });\n }\n\n /** Remove a callback previously registered using `subscribe`. */\n unsubscribe(callback) {\n this.#listeners = this.#listeners.filter(f => f != callback);\n }\n\n /** Get sub-value at path, or entire object if none given */\n get(path = []) {\n return path.length == 0 ? this.#data : _.get(this.#data, path);\n }\n\n /** Get sub-value specified by topic */\n getByTopic(topic) {\n return this.get(topicToPath(topic));\n }\n\n /** Filter the object using path with wildcards */\n filter(path) {\n const rtv = JSON.parse(JSON.stringify(this.get()));\n selectFromObject(rtv, path);\n return rtv;\n }\n\n /** Filter the object using topic with wildcards */\n filterByTopic(topic) {\n return this.filter(topicToPath(topic));\n }\n\n /** For each topic match, invoke the callback with the value, path, and match\n just like subscribePath, but on the current data rather than future changes. */\n forMatch(topic, callback) {\n const path = topicToPath(topic);\n this.forPathMatch(path, callback);\n }\n\n /** For each path match, invoke the callback with the value, path, and match\n just like subscribePath */\n forPathMatch(path, callback) {\n forMatchIterator(this.get(), path, callback);\n }\n};\n\nmodule.exports = {\n DataCache, updateObject\n}", "const dataCache = require('./DataCache');\nconst tools = require('./tools');\n\nmodule.exports = { ...dataCache, ...tools };", "'use strict';\n\nconst _ = require('lodash');\n\nconst { mqttParsePayload, topicMatch, topicToPath, pathToTopic,\ntoFlatObject, getLogger, mergeVersions, parseMQTTTopic, isSubTopicOf,\nversionCompare, encodeTopicElement, visitAncestor, getRandomId }\n = require('./common');\nconst { DataCache } = require('./datacache/DataCache');\n\n\nconst log = getLogger('MqttSync');\nlog.setLevel('info');\n\nconst HEARTBEAT_TOPIC = '$SYS/broker/uptime';\nconst specialKey = '$_'; // special key to reify \"value\" in publishedMessages\n\nconst noop = () => {};\n\n/* clone a mqtt payload, if necessary */\nconst clone = (payload) => {\n if (typeof payload == 'object') {\n return JSON.parse(JSON.stringify(payload));\n } else {\n return payload;\n }\n};\n\n/* return new string that ends in /# for sure */\nconst ensureHashSuffix = (topic) =>\n topic.endsWith('/#') ? topic :\n ( topic.endsWith('/') ? topic.concat('#') :\n topic.concat('/#') );\n\n/* given a path, replace any double slashes, '//', with single ones */\nconst resolveDoubleSlashes = (path) => path.replace(/\\/\\//g, '/');\n\n\n/** A class that combines DataCache and MQTT to implement a data synchronization\nfeature over the latter. Relies on retained messages in mqtt for persistence.\n* @param {object} options\n* @param {object} options.mqttClient - An already connected mqtt.js client.\n* @param {boolean} [options.ignoreRetain] - retain all messages, ignorant of the retain\n* flag.\n* @param {number} [options.sliceTopic] - a number indicating at what level to\n* slice the topic, i.e., only use a suffix. Used in robot-capabilities to slice\noff the topic prefix (namespaces).\n* @param {array} [options.migrate] - an array of objects of the form\n* `{topic, newVersion, level}`. Only meaningful in the cloud. Instructs MQTTSync\nto first migrate existing topics to a new version namespace, publishing at the\ndesignated level down from the version level. For example:\n```js\n[{ topic: `/myorg/mydevice/@local/my-cap/+/config`,\n newVersion: this.version,\n level: 1\n}]\n```\nWould migrate any existing data in the capability's `config` namespace to the\ncurrent version of the package, publishing at the `config/+` level (rather than\natomically at the config level itself).\n* @param {function} [options.onReady] - A function that is called when the MQTTSync\nclient is ready and has completed any requested migrations.\n* @param {function} [options.onChange] - A function that is called any time there\nis a change to the shared data. This is not usually used. It's usually better to\nuse the finer grained `MqttSync.data.subscribePath` instead, that allows you to\nsubscribe to changes just on a specific sun-object instead, see DataCache.\n*/\nclass MqttSync {\n\n data = new DataCache();\n\n /* Directory of paths we've subscribed to in this class; this matters\n because the same mqtt client may have subscriptions to paths that we don't\n care to store (sync). */\n subscribedPaths = {};\n\n publishedPaths = {}; // not used in atomic mode\n\n /* Store messages retained on mqtt so we can publish what is necessary to\n achieve the \"should-be\" state. Note that we cannot use a structured document\n for storing these publishedMessages since we need to be able to store separate\n values at non-leaf nodes in the object (just like mqtt, where you can have\n /a/b = 1 and /a/b/c = 1 at the same time). Note: not used in atomic mode.\n Note: we use specialKey in this DataCache to allow overlapping\n topics (e.g., `/a/b/$_ = 1` and `/a/$_ = {b: 2}`)\n */\n publishedMessages = new DataCache();\n\n /* The order in which we send retained messages matters, which is why we use\n a queue for sending things. Note that we here use the property of Map that it\n remembers insertion order of keys. */\n publishQueue = new Map();\n\n /* We need to keep a record of all received topics (not messages) so far in\n case we want to clear any of them. */\n receivedTopics = new Set();\n\n /* List of callbacks waiting for next heartbeat, gets purged with each\n heartbeat */\n heartbeatWaitersOnce = [];\n\n heartbeats = 0;\n\n beforeDisconnectHooks = [];\n\n rpcHandlers = {}; // handlers for incoming RPC requests\n rpcCallbacks = {}; // callback for RPC requests we've sent\n\n constructor({mqttClient, onChange, ignoreRetain, migrate, onReady,\n sliceTopic, onHeartbeatGranted }) {\n\n this.mqtt = mqttClient;\n this.sliceTopic = sliceTopic;\n\n this.mqtt.on('message', (topic, payload, packet) => {\n const payloadString = payload && payload.toString()\n // log.debug('got message', topic, payloadString.slice(0, 180),\n // payloadString.length > 180 ? `... (${payloadString.length} bytes)` : '',\n // packet.retain);\n\n if (topic == HEARTBEAT_TOPIC) {\n if (this.heartbeats > 0) { // ignore initial heartbeat (retained)\n this.heartbeatWaitersOnce.forEach(cb => cb());\n this.heartbeatWaitersOnce = [];\n }\n if (this.heartbeats == 1 && !migrate && onReady) onReady();\n this.heartbeats++;\n\n } else {\n if (payload.length == 0)\n this.receivedTopics.delete(topic)\n else this.receivedTopics.add(topic);\n\n // Do NOT parse payload just yet, since it may be binary and ignored by us\n\n let path = topicToPath(topic);\n log.debug('processing message', topic);\n if (sliceTopic) {\n path = path.slice(sliceTopic);\n topic = pathToTopic(path);\n }\n\n if (this.rpcHandlers[topic]) {\n const json = mqttParsePayload(payload);\n this.handleRPCRequest(topic, json);\n\n } else if (this.rpcCallbacks[topic]) {\n const json = mqttParsePayload(payload);\n this.handleRPCResponse(topic, json);\n\n } else if (packet.retain || ignoreRetain) {\n\n if (this.isPublished(topic)) {\n const json = mqttParsePayload(payload);\n // store plain messages, still stored in a structure, but values are\n // not interpreted; we just store them to undo them if necessary, e.g.,\n // for switching between atomic and non-atomic subdocuments\n // log.trace('setting publishedMessages', topic);\n this.publishedMessages.updateFromArray([...path, specialKey], json);\n\n // this.pubData.update(topic, json);\n // Still need to update the data so that we can detect changes we make\n // and publish them. But we need to break the reaction-chain to avoid\n // loops, so tag this update with 'published' and then ignore those\n // updates in this.publish.\n this.data.update(topic, json, {external: true});\n\n } else if (this.isSubscribed(topic)) {\n const json = mqttParsePayload(payload);\n\n log.debug('applying received update', topic);\n const changes = this.data.update(topic, json, {external: true});\n onChange && Object.keys(changes).length > 0 && onChange(changes);\n }\n }\n // else: do not try to parse it, it might be a binary message sent\n // directly using the client (with or without retain)\n }\n });\n\n this.mqtt.subscribe(HEARTBEAT_TOPIC, {rap: true}, (err, granted) => {\n log.debug(HEARTBEAT_TOPIC, {granted});\n granted && granted.length > 0 && onHeartbeatGranted?.();\n });\n\n migrate?.length > 0 && this.migrate(migrate, () => {\n log.debug('done migrating');\n onReady && this.waitForHeartbeatOnce(onReady);\n });\n }\n\n /**\n * Publish all values at the given level of the given object under the given\n * topic (plus sub-key, of course).\n * TODO: Is this OK, or do we need to go through this.publish?\n */\n publishAtLevel(topic, value, level) {\n log.debug(`publishingAtLevel ${level}`, topic, value);\n\n if (level > 0) {\n _.forEach(value, (subValue, subKey) => {\n const subTopic = `${topic}/${encodeTopicElement(subKey)}`;\n log.debug(`publishing ${subTopic}`);\n this.publishAtLevel(subTopic, subValue, level - 1);\n });\n } else {\n this.mqtt.publish(topic, JSON.stringify(value), {retain: true}, (err) => {\n err && log.warn('Error when publishing migration result', err);\n });\n }\n }\n\n /** Migrate a list of `{topic, newVersion, transform}`. The version number in\n * topic will be ignored, and all versions' values will be merged, applied in\n * order, such that the latest version is applied last. `topic` may include\n * wildcards in the part before the version number but not after.\n *\n * Example:\n * ```js\n * mqttSync.migrate([{topic: '/+/dId/@scope/capname/+/b', newVersion: '1.2.0'}]\n * ```\n */\n migrate(list, onReady = undefined) {\n\n let toGo = list.length;\n if (toGo == 0) {\n onReady && onReady(); // in case an empty list was given\n return;\n }\n\n /* called each time one item is done */\n const oneDown = () => --toGo == 0 && onReady && onReady();\n\n list.forEach(({topic, newVersion, transform = undefined, flat = false,\n level = 0}) => {\n log.debug('migrating', topic, newVersion);\n const {organization, device, capability, sub} = parseMQTTTopic(topic);\n const prefix = `/${organization}/${device}/${capability}`;\n\n const suffix = sub.length == 0 ? '/#' : pathToTopic(sub);\n // suffix will have a leading slash\n const subTopic = `${prefix}/+${suffix}`;\n\n this.subscribe(subTopic, (err) => {\n if (err) {\n log.warn('Error during migration', err);\n oneDown();\n return;\n }\n\n const all = {};\n this.waitForHeartbeatOnce(() => {\n\n // for each match (prefix can include wildcards) merge everything\n this.data.forMatch(prefix, (value, path, match) => {\n // an actual (ground, aka. no wildcard) prefix\n const groundPrefix = pathToTopic(path);\n\n log.debug('got heartbeat', {prefix, topic, subTopic, suffix}, groundPrefix, value);\n if (!value) {\n // no data to migrate\n return;\n }\n // collect for cleanup\n Object.assign(all, value);\n\n const merged = mergeVersions(value, suffix, {maxVersion: newVersion});\n // get suffix in merged\n const suffixMergedValue = _.get(merged, topicToPath(suffix));\n log.debug({value, suffix, merged, suffixMergedValue});\n // ^ this will need to change to support wild-cards in suffix\n const transformed = transform ? transform(suffixMergedValue) :\n suffixMergedValue;\n\n // Publish the transformed value under the ground prefix as\n // `newVersion/suffix`\n const newTopic =\n resolveDoubleSlashes(`${groundPrefix}/${newVersion}/${suffix}`);\n log.debug('publishing merged', newTopic);\n\n if (flat) {\n const flatObj = toFlatObject(transformed);\n const newPath = topicToPath(newTopic);\n _.forEach(flatObj, (value, key) => {\n const keyTopic = pathToTopic(newPath.concat(topicToPath(key)));\n // TODO: Is this OK, or do we need to go through this.publish?\n this.mqtt.publish(keyTopic, JSON.stringify(value),\n {retain: true}, (err) => {\n err && log.warn(\n `Error when publishing migration result for ${key}`, err);\n });\n });\n\n } else {\n this.publishAtLevel(newTopic, transformed, level);\n }\n });\n\n this.unsubscribe(subTopic);\n\n if (Object.keys(all).length == 0) {\n // no data to migrate\n oneDown();\n return;\n }\n\n this.waitForHeartbeatOnce(() => {\n // now clear this suffix in the old version space\n const oldVersions = Object.keys(all).filter(v =>\n versionCompare(v, newVersion) < 0);\n // log.debug({oldVersions});\n\n const prefixesToClear = oldVersions.map(oldV =>\n resolveDoubleSlashes(`${prefix}/${oldV}/${suffix}`));\n\n this.clear(prefixesToClear);\n oneDown();\n });\n });\n });\n });\n }\n\n /** Delete all retained messages in a certain topic prefix, waiting for\n a mqtt broker heartbeat to collect existing retained. Use with care, never\n delete topics not owned by us. Harmless within capabilities, which are\n namespaced already.\n\n `options.filter(topic)`: a function that can be provided to further,\n programmatically filter the set of topics to clear, e.g., to onlt clear\n topics of old versions.\n\n Note: This may not yet work in robot-capabilities, since the subscription\n prefix and received topic prefix don't match (the device prefix is added to\n subscription by localMQTT.\n */\n clear(prefixes, callback = undefined, options = {}) {\n\n // If prefixes are empty, just skip (call callback and return)\n if (!prefixes || prefixes.length == 0) {\n log.warn('clear was given no prefixes');\n callback?.(0);\n return;\n }\n\n const toDelete = [];\n const collectToDelete = (topic) => {\n // there may be other mqtt subscriptions running, filter by topic\n prefixes.forEach(prefix =>\n topicMatch(`${prefix}/#`, topic)\n && (!options.filter || options.filter(topic))\n && toDelete.push(topic)\n );\n }\n this.mqtt.on('message', collectToDelete);\n // this only collects new topics, not those we've already received\n\n // subscribe to all\n prefixes.forEach(prefix => {\n if (typeof prefix == 'string') {\n this.mqtt.subscribe(`${prefix}/#`);\n } else {\n log.warn('Ignoring', prefix, 'since it is not a string.');\n }\n });\n\n // add matching topics we already know off:\n this.receivedTopics.forEach(collectToDelete);\n\n // value to use to clear, depending on node.js vs. browser\n const nullValue = (typeof Buffer != 'undefined' ? Buffer.alloc(0) : null);\n\n this.waitForHeartbeatOnce(() => {\n this.mqtt.removeListener('message', collectToDelete);\n prefixes.forEach(prefix => this.mqtt.unsubscribe(prefix));\n\n const count = toDelete.length;\n log.info(`clearing ${count} retained messages from ${prefixes}`);\n toDelete.forEach(topic => {\n this.mqtt.publish(topic, nullValue, {retain: true});\n });\n\n callback && callback(count);\n });\n };\n\n\n /** register a callback for the next heartbeat from the broker */\n waitForHeartbeatOnce(callback) {\n // need to wait a tick, in case we are still in the callback tree\n // of a previous heartbeat waiter\n setTimeout(() => this.heartbeatWaitersOnce.push(callback), 1);\n }\n\n /** check whether we are subscribed to the given topic */\n isSubscribed(topic) {\n return Object.keys(this.subscribedPaths).some(subscribedTopic =>\n topicMatch(subscribedTopic, topic));\n }\n\n /** Check whether we are publishing the given topic in a non-atomic way.\n This is used to determine whether to store the published value or not. */\n isPublished(topic) {\n return Object.keys(this.publishedPaths).some(subscribedTopic =>\n topicMatch(subscribedTopic, topic) &&\n !this.publishedPaths[subscribedTopic].atomic\n );\n }\n\n /** Subscribe to the given topic (and all sub-topics). The callback will\n indicate success/failure, *not* a message on the topic. */\n subscribe(topic, callback = noop) {\n topic = ensureHashSuffix(topic);\n log.debug('subscribing to', topic);\n if (this.subscribedPaths[topic]) {\n log.debug('already subscribed to', topic);\n callback();\n return;\n }\n\n this.mqtt.subscribe(topic, {rap: true}, (err, granted) => {\n log.debug('subscribe', topic, 'granted:', granted);\n if (granted && granted.some(grant => grant.topic == topic && grant.qos < 128)) {\n // granted\n this.subscribedPaths[topic] = 1;\n callback(null);\n } else {\n // let user know (somehow) when we don't get permission\n callback(`not permitted to subscribe to topic ${topic}, ${JSON.stringify(granted)}`);\n }\n });\n }\n\n unsubscribe(topic) {\n topic = ensureHashSuffix(topic);\n if (this.subscribedPaths[topic]) {\n this.mqtt.unsubscribe(topic);\n delete this.subscribedPaths[topic];\n }\n }\n\n /** Publish retained to MQTT, store as published, and return a promise */\n _actuallyPublish(topic, value) {\n // return new Promise((resolve, reject) =>\n // this.mqtt.publish(topic,\n // value == null ? null : JSON.stringify(value), // aka \"unparse payload\"\n // {retain: true},\n // (err) => {\n // // Note that this returns optimistically at QoS 0, and no error occurs\n // // even when we are not allowed to publish this topic/message, see\n // // https://github.com/mqttjs/MQTT.js/#publish. Only when the client\n // // disconnects it seems.\n // if (err) {\n // log.warn('error in _actuallyPublish:', err);\n // reject(err);\n // // TODO: if this happens, we may need to force a full-sync\n // } else {\n // resolve();\n // }\n // }));\n\n if (!this.mqtt.connected) {\n log.warn('not connected, not publishing', topic);\n return false;\n }\n log.debug('actually publishing', topic);\n this.mqtt.publish(topic,\n value == null ? null : JSON.stringify(value), // aka \"unparse payload\"\n {retain: true});\n return true;\n }\n\n /** Send all items in the queue in sequence, if any and if not already\n running. */\n // async _processQueue() {\n // if (this._processing) return; // already running (and probably waiting)\n //\n // this._processing = true;\n // while (this.publishQueue.length > 0) {\n // const {topic, value} = this.publishQueue.shift();\n // await this._actuallyPublish(topic, value);\n // }\n // this._processing = false;\n // }\n\n // when using Map\n _processQueue_rec(cb) {\n if (this.publishQueue.size > 0) {\n const [topic, value] = this.publishQueue.entries().next().value;\n // this.publishQueue.delete(topic);\n // this._actuallyPublish(topic, value).then(\n // () => this._processQueue_rec(cb),\n // cb); // always call cb, even in rejection case\n if (this._actuallyPublish(topic, value)) {\n this.publishQueue.delete(topic);\n this._processQueue_rec(cb);\n } else {\n // try again soon\n setTimeout(() => this._processQueue_rec(cb), 5000);\n }\n } else {\n cb();\n }\n }\n\n _processQueue() {\n if (this._processing) return; // already running (and probably waiting)\n\n this._processing = true; // semaphore\n this._processQueue_rec(() => this._processing = false);\n }\n\n /** Set delay between processing of publishing queue in milliseconds. This\n allows you to effectively throttle the rate at which this instance will\n publish changes. Note that updates to a topic already in the queue will not\n cause multiple publications. Only the latest value will be published.\n @param {number} [delay] - Number of milliseconds to wait between processing\n of publish queue.\n */\n setThrottle(delay) {\n this._processQueueThrottled =\n _.throttle(this._processQueue.bind(this), delay);\n }\n\n /** Clear the set throttling delay. */\n clearThrottle() {\n delete this._processQueueThrottled;\n }\n\n addToQueue(topic, value) {\n // this.publishQueue.push({topic, value});\n this.publishQueue.set(topic, value);\n }\n\n /** Add to publication queue */\n _enqueue(topic, value) {\n log.debug('enqueuing', topic);\n this.addToQueue(topic, value);\n if (this._processQueueThrottled) {\n this._processQueueThrottled();\n } else {\n this._processQueue();\n }\n // yes, this is optimistic, but if we don't, then upcoming changes\n // may not work as expected (e.g., when switching from flat to atomic to flat)\n const path = topicToPath(topic);\n this.publishedMessages.updateFromArray([...path, specialKey],\n value == null ? null : clone(value));\n }\n\n /** Register a listener for path in data. Make sure to populate the data\n before calling this or set the data all at once afterwards.\n\n With option \"atomic\" this will always send the whole sub-document,\n not flat changes. Useful, e.g., for desiredPackages, see\n https://github.com/chfritz/transitive/issues/85.\n\n @return true if publication added (false, e.g., when already present)\n */\n publish(topic, options = {atomic: false}) {\n topic = ensureHashSuffix(topic);\n\n if (_.isEqual(this.publishedPaths[topic], options)) {\n return false;\n // avoid double subscription\n }\n this.publishedPaths[topic] = options;\n\n if (options.atomic) {\n // this case is quite simple\n this.data.subscribePath(topic, (value, key, matched, tags) => {\n // do not re-publish changes received from external:\n if (tags?.external) return;\n\n log.debug('processing change (atomic)', key, topic);\n // instantiate topic according to key (topic may have wildcards)\n const topicWithoutHash = topic.slice(0, topic.length - 2);\n const groundedTopic = pathToTopic(\n // get length of topic (how many levels of selectors), get that many\n // levels from key prefix\n topicToPath(key).slice(0, topicToPath(topicWithoutHash).length)\n );\n this._enqueue(groundedTopic, this.data.getByTopic(groundedTopic));\n });\n return true;\n }\n\n this.mqtt.subscribe(topic);\n\n // second: keep them up to date by publishing updates as changes happen\n this.data.subscribePath(topic, (value, key, matched, tags) => {\n if (tags?.external) return;\n\n log.debug('processing change', key);\n\n /* First: establish clarity by ensuring that the object defined by the\n currently present retained messages under this path accurately reflect\n the current value of this.data (and if not, publish what is necessary to\n create consistency). */\n // first, clear/replace all messages below or above this sub-path (if any)\n const path = topicToPath(key);\n\n // Check flat to atomic\n const publishedSub = this.publishedMessages.get(path);\n _.each(publishedSub, (oldSubVal, oldSubKey) => {\n if (oldSubKey == specialKey) return true;\n // We are going from flat to atomic, i.e., we are publishing at a\n // higher level than before: clear out old sub-keys.\n\n // Find all sub-sub-keys that end in `specialKey`:\n const toClear = Object.keys(toFlatObject(oldSubVal))\n .filter(subkey => subkey.endsWith(specialKey));\n\n log.debug('flat->atomic: ', {toClear}, oldSubKey);\n // Clear them all:\n toClear.forEach(oldSubSubKey => {\n const oldKey = oldSubSubKey.slice(0, -(specialKey.length + 1));\n const clearKey = `${key}/${oldSubKey}/${oldKey}`\n // log.debug('flat->atomic: clear', clearKey);\n this._enqueue(clearKey, null);\n });\n });\n\n // Check atomic to flat\n const published = this.publishedMessages.get();\n visitAncestor(published, path.slice(0, -1), (subObj, prefix) => {\n const oldVal = subObj[specialKey];\n if (oldVal && _.isObject(oldVal)) {\n log.debug('atomic->flat', {oldVal});\n // A parent topic has been published. We are going from atomic to\n // separate values: need to transform existing sub-document to flat\n // values.\n\n // Remove the old published (atomic) message:\n const prefixTopic = pathToTopic(prefix);\n this._enqueue(prefixTopic, null);\n\n // Now re-add as separate flat messages\n const flat = toFlatObject(oldVal);\n _.each(flat, (flatValue, flatKey) => {\n const oldFlatKey = `${prefixTopic}${flatKey}`;\n this._enqueue(oldFlatKey, flatValue);\n })\n }\n });\n\n /* We need to first wait until all of the above messages are out;\n otherwise replacing an atomic `/a = {c: 1}` with `/a/c = 2` would create\n a race condition where it is not clear which message, the replacement\n c = 1 or the new c = 2, would be sent last (and hence retained). That's\n why we use a publishing queue in this class.\n */\n this._enqueue(key, value);\n return true;\n });\n }\n\n /** Run all registered hooks before disconnecting */\n beforeDisconnect() {\n this.beforeDisconnectHooks.forEach(fn => fn(this));\n }\n\n /** Register a new hook to be called before disconnecting */\n onBeforeDisconnect(fn) {\n this.beforeDisconnectHooks.push(fn);\n }\n\n /* --------------------------------------------------------------------------\n * Remote Procedure Calls (RPC)\n */\n\n /* Handle RPC requests */\n async handleRPCRequest(topic, json) {\n log.debug('handling RPC request for', topic, json);\n const handler = this.rpcHandlers[topic];\n const result = handler(json.args);\n\n const responseTopic = `${topic.replace('/request', '/response')}/${json.id}`;\n\n if (result instanceof Promise) {\n result.then( resultValue => this.mqtt.publish(responseTopic,\n JSON.stringify({ id: json.id, result: resultValue }),\n {retain: false, qos: 2}));\n } else {\n this.mqtt.publish(responseTopic,\n JSON.stringify({ id: json.id, result }),\n {retain: false, qos: 2});\n }\n }\n\n /* Handle RPC response */\n handleRPCResponse(topic, json) {\n log.debug('handle RPC response', topic, json);\n this.rpcCallbacks[topic](json.result);\n delete this.rpcCallbacks[topic];\n this.mqtt.unsubscribe(topic);\n }\n\n /** Register an RPC request handler. Example:\n * ```js\n * mqttSync.register('/mySquare', arg => {\n * log.debug('running /mySquare with args', arg);\n * return arg * arg;\n * });\n * ```\n * Note that the command topic needs to be in the capabilities namespace like\n * any other topic. In robot capabilities, as usual, these can start in `/`\n * because the local mqtt bridge operated by the robot agent will place all\n * topics in their respective namespace. In the cloud and on the web you will\n * need to use the respective namespace, i.e.,\n * `/orgId/deviceId/@scope/capName/capVersion/`.\n *\n * #### Async/Await\n * Yes, you can make the handler `async` and use `await` inside of it. This\n * will be handled correctly, i.e., MqttSync will await the result of the\n * handler before responding to the RPC request client.\n */\n register(command, handler) {\n log.debug('registering RPC handler for', command);\n const requestTopic = `${command}/request`;\n\n this.rpcHandlers[requestTopic] = handler;\n this.mqtt.subscribe(requestTopic, {rap: true, qos: 2}, (err, granted) => {\n if (err) {\n log.warn(`Error subscribing to RPC topic ${requestTopic}`, err);\n } else if (granted && granted.length == 0) {\n log.warn(`Not allowed to subscribe to RPC topic ${requestTopic}`);\n }\n });\n }\n\n /** Make an RPC request. Example:\n * ```js\n * mqttSync.call('/mySquare', 11, result => {\n * log.debug(`Called /mySquare with arg 11 and got ${result}`);\n * });\n * ```\n * Alternative you can omit the callback and use async/await:\n * ```js\n * const result = await mqttSync.call('/mySquare', 11);\n * log.debug(`Called /mySquare with arg 11 and got ${result}`);\n * ```\n * See the note about namespaces in `register`.\n *\n * Note: It is your responsibility to only call methods that exist (have been\n * registered). Calling a non-existent command just hangs.\n */\n call(command, args, callback = undefined) {\n const id = getRandomId();\n\n const responseTopic = `${command}/response/${id}`;\n this.mqtt.subscribe(responseTopic, {rap: true, qos: 2}, (err, granted) => {\n if (err) {\n log.warn(`Error subscribing to RPC response topic ${responseTopic}`, err);\n } else if (granted && granted.length == 0) {\n log.warn(`Not allowed to subscribe to RPC response topic ${responseTopic}`);\n }\n });\n\n const requestTopic = `${command}/request`\n log.debug('calling RPC', requestTopic);\n this.mqtt.publish(requestTopic, JSON.stringify({ id, args }),\n {retain: false, qos: 2});\n\n if (callback) {\n this.rpcCallbacks[responseTopic] = callback;\n } else {\n return new Promise((resolve, reject) => {\n this.rpcCallbacks[responseTopic] = resolve;\n });\n }\n }\n}\n\nmodule.exports = MqttSync;\n", "export * from './client/shared.jsx';\nexport * from './client/hooks.jsx';\nexport * from './client/client';\n", "import React, { useState, useEffect, useMemo, useRef } from 'react';\nimport { Button, Accordion, AccordionContext, Card, Badge, Dropdown,\n ButtonGroup, Form } from 'react-bootstrap';\nimport ReactWebComponent from './react-web-component';\n\nimport { parseCookie, decodeJWT } from './client';\nimport { useCapability } from './hooks';\n\nconst F = React.Fragment;\n\nconst styles = {\n badge: {\n width: '4em'\n },\n code: {\n color: '#700',\n borderLeft: '3px solid #aaa',\n padding: '0.5em 0px 0.5em 2em',\n backgroundColor: '#f0f0f0',\n borderRadius: '4px',\n marginTop: '0.5em',\n },\n inlineCode: {\n color: '#700',\n margin: '0px 0.5em 0px 0.5em',\n },\n\n selector: {\n marginBottom: '1em',\n width: '100%',\n },\n field: {\n borderBottomLeftRadius: '0px',\n borderTopLeftRadius: '0px',\n marginBottom: '1em',\n flex: '100 1 10000em',\n },\n sourceForm: {\n // display: 'flex',\n // gap: '1rem',\n display: 'inline-block',\n width: '100%',\n },\n\n};\n\nconst levelBadges = [\n <Badge bg=\"success\" style={styles.badge}>OK</Badge>,\n <Badge bg=\"warning\" style={styles.badge}>Warn</Badge>,\n <Badge bg=\"danger\" style={styles.badge}>Error</Badge>,\n <Badge bg=\"secondary\" style={styles.badge}>Stale</Badge>,\n];\n\n/* The right badge for the level */\nexport const LevelBadge = ({level}) => levelBadges[level] || <span>{level}</span>;\n\n/** Reusable component for showing code */\nexport const Code = ({children}) => <pre style={styles.code}>\n {children}\n</pre>;\n\nexport const InlineCode = ({children}) => <tt style={styles.inlineCode}>\n {children}\n</tt>;\n\n\nconst intervals = {};\n\n/** A Timeout component: removes the children once time runs out */\nexport const TimerContext = React.createContext({});\nexport const Timer = ({duration, onTimeout, onStart, setOnDisconnect, children}) => {\n duration = duration || 60;\n const [timer, setTimer] = useState(duration);\n const [running, setRunning] = useState(false);\n const id = useMemo(() => Math.random().toString(36).slice(2), []);\n\n const stop = () => {\n console.log('stopping timer for', id);\n onTimeout && setTimeout(onTimeout, 1);\n clearInterval(intervals[id]);\n intervals[id] = null;\n setRunning(false);\n };\n\n const startTimer = () => {\n const interval = intervals[id];\n console.log(interval, intervals, timer);\n if (!interval && timer > 0) {\n setRunning(true);\n intervals[id] = setInterval(() =>\n setTimer(t => {\n if (--t > 0) {\n return t;\n } else {\n stop();\n }\n }), 1000);\n onStart && setTimeout(onStart, 1);\n }\n\n return stop;\n };\n\n useEffect(() => { timer > 0 && !running && startTimer() }, [timer]);\n\n useEffect(() => stop, []);\n\n setOnDisconnect && setOnDisconnect(() => {\n // call on disconnect of the web component\n stop()\n });\n\n const reset = () => setTimer(duration);\n\n return <TimerContext.Provider value={{reset, duration, timer}}>\n {timer > 0 ? <div>\n {children}\n {timer < 60 && <div className=\"tr-timer-counter\">Timeout in: {timer} seconds</div>}\n </div> :\n <div className=\"tr-timer-timeout\">Timed out. <Button onClick={reset}>\n Resume\n </Button>\n </div>}\n </TimerContext.Provider>;\n};\n\n\n/** Dynamically load and use the Transitive web component specified in the JWT.\n* Embedding Transitive components this way also enables the use of functional\n* and object properties, which get lost when using the custom element (Web\n* Component) because HTML attributes are strings.\n* Example:\n* ```jsx\n* <TransitiveCapability jwt={jwt}\n* myconfig={{a: 1, b: 2}}\n* onData={(data) => setData(data)}\n* onclick={() => { console.log('custom click handler'); }}\n* />\n* ```\n*\n* Always loads the capability specified in the JWT and will default to the\n* main component for that JWT (`-device` or `-fleet`). To specify a secondary\n* component offered by the capability specify `component`, e.g., to load\n* `webrtc-video-supervisor` instead of `webrtc-video-device`, provide a device\n* JWT for webrtc-video and use:\n* ```jsx\n* <TransitiveCapability jwt={jwt}\n* component='webrtc-video-supervisor'\n* auto=\"true\"\n* />\n* ```\n*/\nexport const TransitiveCapability = ({\n jwt, host = 'transitiverobotics.com', ssl = true, ...config\n }) => {\n\n const assertPresent = (value, name) => {\n if (!value) throw new Error(`JWT is missing ${name}`);\n };\n\n const {id, device, capability} = decodeJWT(jwt);\n // Throw an error when any of the above payload is missing\n assertPresent(id, 'id');\n assertPresent(device, 'device');\n assertPresent(capability, 'capability');\n\n const type = device == '_fleet' ? 'fleet' : 'device';\n const capName = capability.split('/')[1];\n const name = `${capName}-${type}`;\n const component = config.component || name;\n\n const { loaded } = useCapability({\n capability,\n name,\n userId: id || config.userId, // accept both id and userId, see #492\n deviceId: device,\n host,\n ssl\n });\n\n const ref = useRef();\n // Attach functional and object properties to the component when ready and\n // on change\n useEffect(() => {\n ref.current?.instance?.setState(s =>\n ({ ...s, id, jwt, host, ssl, ...config }));\n }, [ref.current, loaded, id, jwt, host, ssl, ...Object.values(config)]);\n\n // Disrupt the reactive chain of the MutationObserver to the customElement,\n // so we are not competing with it for updating the props.\n const propClone = useMemo(() => ({id, jwt, host, ssl, ...config}), []);\n\n if (!loaded) return <div>Loading {name}</div>;\n return React.createElement(component, {...propClone, ref});\n };\n\n\n/** A simple error boundary. Usage:\n* ```jsx\n* <ErrorBoundary message=\"Something went wrong\">\n* <SomeFlakyComponent />\n* </ErrorBoundary>\n* ```\n*/\nexport class ErrorBoundary extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n hasError: false,\n messages: [],\n };\n }\n\n static getDerivedStateFromError(error) {\n return { hasError: true };\n }\n\n componentDidCatch(error, errorInfo) {\n console.warn('ErrorBoundary caught:', error, errorInfo);\n this.setState(({messages}) => ({messages: [...messages, error.message]}));\n }\n\n render() {\n return (this.state.hasError ? <div>\n Error: {this.props.message || this.state.messages?.join(', ')\n || 'Something went wrong here.'}\n </div>\n : this.props.children);\n }\n};\n\n\nexport const CapabilityContext = React.createContext({});\n\n/* Only used internally: the actual context provider, given the loaded module */\nconst LoadedCapabilityContextProvider = (props) => {\n const {children, jwt, id, host, ssl, loadedModule} = props;\n\n const context = loadedModule.provideContext?.({\n jwt, id, host, ssl, appReact: React\n });\n\n return <CapabilityContext.Provider value={{ ...context }}>\n {children}\n </CapabilityContext.Provider>;\n};\n\n/**\n* Context provider for capabilities. Use this to access the front-end API\n* provided by some capabilities. Example:\n* ```jsx\n* <CapabilityContextProvider jwt={jwt}>\n* <MyROSComponent />\n* </CapabilityContextProvider>\n* ```\n* where `jwt` is a JWT for a capability that exposes a front-end API. Then use\n* `useContext` in `MyROSComponent` to get the exposed data and functions, e.g.:\n* ```jsx\n* const MyROSComponent = () => {\n* const { ready, subscribe, data } = useContext(CapabilityContext);\n* // When ready, subscribe to the `/odom` topic in ROS1\n* useEffect(() => { ready && subscribe(1, '/odom'); }, [ready]);\n* return <pre>{JSON.stringify(data, true, 2)}</pre>;\n* }\n* ```\n* Where `ready`, `subscribe`, and `data` are reactive variables and functions\n* exposed by the capability of the provided JWT. In this example, the latest\n* message from the subscribed ROS topics will be available in the capabilities\n* namespace in `data`.\n* @param {object} props\n*/\nexport const CapabilityContextProvider =\n ({children, jwt, host = undefined, ssl = undefined}) => {\n\n const {id, device, capability} = decodeJWT(jwt);\n const type = device == '_fleet' ? 'fleet' : 'device';\n const capName = capability.split('/')[1];\n const name = `${capName}-${type}`;\n\n const {loaded, loadedModule} = useCapability({\n capability,\n name,\n userId: id,\n deviceId: device,\n appReact: React,\n host,\n ssl\n });\n\n if (!loadedModule) return <div>Loading {capability}</div>;\n return <LoadedCapabilityContextProvider {...{jwt, id, host, ssl, loadedModule}}>\n {children}\n </LoadedCapabilityContextProvider>;\n };\n\n\n/* whether or not the given react component allows refs, i.e., is either\n * a functional component wrapped with forwardRef or a class component */\nconst componentPermitsRefs = (Component) =>\n (Component.$$typeof == Symbol.for('react.forward_ref'))\n || Component.prototype?.render;\n\n\n/** Create a WebComponent from the given react component and name that is\n* reactive to all attributes. Used in web capabilities. Example:\n* ```js\n* createWebComponent(Diagnostics, 'health-monitoring-device', TR_PKG_VERSION);\n* ```\n*/\nexport const createWebComponent = (Component, name, version = '0.0.0',\n options = {}) => {\n\n // Only create a ref if the component accepts it. This avoids an ugly\n // error in the console when trying to give a ref to a non-forwardRef-wrapped\n // functional component.\n const compRef = componentPermitsRefs(Component) ? React.createRef() : null;\n\n class Wrapper extends React.Component {\n\n onDisconnect = null;\n state = {};\n\n componentDidMount() {\n this.props._element.instance = this;\n this.webComponentConstructed(this.props._element);\n this.props._element.callLifeCycleHook('connectedCallback');\n }\n\n /* function used by `Component` to register a onDisconnect handler */\n setOnDisconnect(fn) {\n this.onDisconnect = fn;\n }\n\n webComponentConstructed(instance) {\n // Observe all changes to attributes and update React state from it\n const observer = new MutationObserver((mutationRecords) => {\n const update = {};\n mutationRecords.forEach(({attributeName}) => {\n update[attributeName] = instance.getAttribute(attributeName);\n });\n this.setState(old => ({...old, ...update}));\n }).observe(instance, { attributes: true });\n }\n\n webComponentDisconnected() {\n // This ensures that the react component unmounts and all useEffect\n // cleanups are called.\n this.setState({_disconnected: true});\n try {\n this.onDisconnect && this.onDisconnect();\n } catch (e) {\n console.log('Error during onDisconnect of web-component', e);\n }\n }\n\n /* method exposed to the wrapped component via prop that allows setting\n * the \"config\" state variable inside the wrapper (not the component\n * itself). This config is retrieved by the portal for inclusion in the\n * embedding instructions. */\n setConfig(config) {\n this.setState({config});\n }\n\n render() {\n const stylesheets = options.stylesheets || [\n // 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css'\n // Bootstrap 5.3.2 css scoped to `.transitive-bs-root`:\n 'https://cdn.jsdelivr.net/gh/transitiverobotics/transitive-utils@0.8.3/web/css/bootstrap_transitive-bs-root.min.css'\n ];\n\n return <div id={`cap-${name}-${version}`}\n className={options.className || 'transitive-bs-root'}>\n <style>\n {stylesheets.map(url => `@import url(${url});`)}\n </style>\n\n {!this.state._disconnected &&\n <Component ref={compRef}\n {...this.props}\n // Important to keep state *after* props for reactivity to work\n {...this.state}\n setOnDisconnect={this.setOnDisconnect.bind(this)}\n setConfig={this.setConfig.bind(this)}\n />}\n </div>;\n }\n };\n\n return ReactWebComponent.create(Wrapper, name, options.shadowDOM || false,\n compRef);\n };\n\n\n/** takes options in the below format and renders a \"tree of dropdowns\" to\n* allow user to select from these options in sequence.\n* Format:\n* ```js\n* { selector: 'video source',\n* field: 'type',\n* options: [\n* { label: 'ROS Topic', // label for this option (in parent selector)\n* value: 'rostopic' // value to use when selected\n* field: 'value', // the field for which options list possible values\n* selector: 'ROS Version', // label for next selector\n* options: [\n* { label: 'ROS1',\n* options: [{\n* label: 'topic1',\n* value: 'topic1'\n* }],\n* }, {\n* label: 'Free form',\n* value: 'free-form',\n* selector: 'Enter text',\n* field: 'textParam',\n* }, {\n* label: 'A Number',\n* value: 'free-form-number',\n* selector: 'Enter number',\n* type: 'number',\n* field: 'numberParam',\n* }, {\n* label: 'A Date',\n* value: 'free-form-date',\n* selector: 'Enter date',\n* type: 'datetime-local',\n* field: 'dateParam',\n* }\n* }\n* },\n* ...\n* ]\n* }\n* ```\n*/\nexport const TreeSelector = (props) => {\n const {selector, field, options} = props.options;\n\n const preselectedOption = props.preselected &&\n options.find(o => _.isEqual(o.value, props.preselected[field]));\n\n // log.debug(preselectedOption);\n\n const [selected, setSelected] = useState(preselectedOption);\n const select = (choice) => {\n setSelected(choice);\n (choice != selected) && props.onSelect?.({\n selected: {[field]: choice.value},\n // Indicate when there are no more options to select from, i.e., the\n // selection is complete:\n complete: !choice.options && !choice.field\n });\n };\n\n const dropDowns = <F>\n <Dropdown as={ButtonGroup} style={styles.selector}>\n <Dropdown.Toggle variant=\"outline-secondary\">\n {selected?.label || selector}\n </Dropdown.Toggle>\n <Dropdown.Menu variant=\"dark\">\n {options.map((option, i) =>\n <Dropdown.Item key={i}\n disabled={option.disabled}\n onClick={() => select(option)}>\n {option.label}\n </Dropdown.Item>\n )}\n </Dropdown.Menu>\n </Dropdown>\n\n {selected?.options ?\n <TreeSelector key={JSON.stringify(selected.value)}\n nested={true}\n options={selected}\n preselected={props.preselected} // TODO: is this right?\n onSelect={(subChoice) => {\n const merged = {\n selected: {[field]: selected.value, ...subChoice.selected},\n complete: subChoice.complete\n };\n props.onSelect?.(merged);\n }}\n />\n : // no options given, just provide an input field\n selected?.field && <Form.Control style={styles.field}\n type={selected.type || 'text'}\n placeholder={selected.selector}\n defaultValue={props.preselected?.[selected.field]}\n onBlur={e => {\n props.onSelect?.({\n selected: {\n [field]: selected.value,\n [selected.field]: e.target.value\n },\n complete: e.target.value.length > 0\n });\n }}\n />\n }\n </F>;\n\n return props.nested ? dropDowns : <div style={styles.sourceForm}>\n <ButtonGroup>\n {dropDowns}\n </ButtonGroup>\n </div>;\n};\n", "// It is OK to use paths outside of this package because webpack will bundle them\nexport * from '../../common/common.js';\nexport * from '../../common/datacache';\nimport MS from '../../common/MqttSync.js';\nexport const MqttSync = MS;\n\n// moved to common\n// export const decodeJWT = (jwt) => JSON.parse(atob(jwt.split('.')[1]));\n\n/** parse document cookies */\nexport const parseCookie = str =>\n str.split(';')\n .map(v => v.split('='))\n .reduce((acc, v) => {\n acc[decodeURIComponent(v[0].trim())] =\n v[1] && decodeURIComponent(v[1].trim());\n return acc;\n }, {});\n\n/** get or post (if body given) json */\nexport const fetchJson = (url, callback, options = {}) => {\n fetch(url, {\n method: options.method || (options.body ? 'post' : 'get'),\n mode: 'cors',\n cache: 'no-cache',\n // Maybe we'll need this (when embedding)?\n // credentials: 'same-origin', // include, *same-origin, omit\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers\n },\n redirect: 'follow',\n referrerPolicy: 'no-referrer',\n body: options.body ? JSON.stringify(options.body) : undefined\n }).then(res => {\n const error = !res.ok &&\n `fetching ${url} failed: ${res.status} ${res.statusText}`;\n res.json()\n .then(data => callback(error, data))\n .catch(err => {\n throw new Error(err);\n });\n }).catch((error) => callback(`error: ${error}`));\n};\n", "import React, { useState, useEffect, useMemo } from 'react';\nimport _ from 'lodash';\n// import mqtt1 from 'mqtt';\nimport mqtt from 'mqtt/dist/mqtt.esm';\n\nimport { decodeJWT, getLogger, clone, pathToTopic, mergeVersions, topicToPath }\n from './client';\nconst MqttSync = require('../../common/MqttSync');\n\nconst log = getLogger('utils-web/hooks');\nlog.setLevel('info');\n\nconst RECONNECT_PERIOD_DEFAULT = 1000; // default time until mqtt retries connecting\nconst RECONNECT_PERIOD_MAX = 20000; // max retry time (after dynamic backoff)\n\n/** Hook for using MqttSync in React.\n* @returns {object} An object `{data, mqttSync, ready, StatusComponent, status}`\n* where:\n* `data` is a reactive data source in React containing all the data received by\n* mqttsync,\n* `mqttSync` is the MqttSync object itself,\n* `ready` indicates when mqttSync is ready to be used (connected and received\n* successfully subscribed to mqtt system heartbeats)\n*/\nexport const useMqttSync = ({jwt, id, mqttUrl, appReact}) => {\n const { useState, useRef, useEffect } = appReact || React;\n\n const [status, setStatus] = useState('connecting');\n const [mqttSync, setMqttSync] = useState();\n const [data, setData] = useState({});\n // True once the subscription to the system heartbeat has been granted.\n const [heartbeatGranted, setHeartbeatGranted] = useState(false);\n\n useEffect(() => {\n const payload = decodeJWT(jwt);\n\n // pre-check validity of JWT, don't use if expired\n const { validity, iat } = payload;\n if (!validity || !iat || (iat + validity) * 1e3 < Date.now()) {\n const error = 'The provided JWT is invalid or expired.';\n log.warn(error, payload);\n setStatus(`error: ${error}`);\n return;\n }\n\n /** Implement dynamic backoff when we fail to connect. */\n let reconnectPeriod = RECONNECT_PERIOD_DEFAULT; // default to start with\n const transformWsUrl = (url, options, client) => {\n options.reconnectPeriod = reconnectPeriod;\n return url;\n }\n\n log.debug('(re-)create mqtt client');\n const client = mqtt.connect(mqttUrl, {\n username: JSON.stringify({id, payload}),\n password: jwt,\n transformWsUrl\n });\n\n // Increase backoff with each close (since we can't actually detect auth\n // errors); stop reconnecting if JWT is invalid by now.\n client.on('close', () => {\n reconnectPeriod = Math.min(reconnectPeriod * 2, RECONNECT_PERIOD_MAX);\n\n if ((iat + validity) * 1e3 < Date.now()) {\n const error = 'MQTT connection closed and the JWT is expired by now. Not reconnecting.';\n log.warn(error, payload);\n setStatus(`error: ${error}`);\n // give up, do not try to reconnect; user needs to provide a fresh JWT.\n client.end();\n } else {\n const msg = `reconnect in ${reconnectPeriod / 1000} s`;\n log.info(msg);\n setStatus(msg);\n }\n });\n\n // reset to default after a successful connection\n client.on('connect', () => {\n reconnectPeriod = RECONNECT_PERIOD_DEFAULT;\n log.debug('MQTT (re-)connected');\n setStatus('connected');\n });\n\n client.once('connect', () => {\n const mqttSyncClient = new MqttSync({\n mqttClient: client,\n ignoreRetain: true,\n onHeartbeatGranted: () => setHeartbeatGranted(true)\n });\n setMqttSync(mqttSyncClient);\n\n // Update data on change. Note: need to clone object to force reaction\n mqttSyncClient.data.subscribe(_.throttle(() =>\n setData(clone(mqttSyncClient.data.get())), 50));\n });\n\n client.on('error', (error) => {\n log.error(error);\n setStatus(`error: ${error}`);\n });\n\n return () => {\n log.info('cleaning up useMQTTSync');\n if (mqttSync && mqttSync.beforeDisconnect) {\n mqttSync.beforeDisconnect();\n mqttSync.waitForHeartbeatOnce(() => client.end());\n } else {\n client.end();\n }\n };\n }, [jwt, id]);\n\n return {\n status,\n // ready: status == 'connected',\n ready: heartbeatGranted,\n StatusComponent: () => <div>{status}</div>,\n mqttSync, // Note: mqttSync.data is not reactive.\n data, // This is a reactive data-source (to use meteor terminology).\n };\n};\n\n/** Hook for using Transitive in React. Connects to MQTT, establishes sync, and\n* exposes reactive `data` state variable. */\nexport const useTransitive =\n ({jwt, id, capability, versionNS, appReact,\n host = 'transitiverobotics.com', ssl = true }) => {\n\n const [scope, capabilityName] = capability.split('/');\n\n const { device } = decodeJWT(jwt);\n const prefixPath = [id, device, scope, capabilityName];\n const prefix = pathToTopic(prefixPath);\n const prefixPathVersion = [...prefixPath, versionNS];\n const prefixVersion = pathToTopic(prefixPathVersion);\n\n const mqttUrl = `${ssl && JSON.parse(ssl) ? 'wss' : 'ws'}://mqtt.${host}`;\n const fromMqttSync = useMqttSync({ jwt, id, mqttUrl, appReact });\n\n return {...fromMqttSync, device, prefixPath, prefix, prefixPathVersion,\n prefixVersion};\n };\n\n\n/** Subscribe to MqttSync topics using the provided JWT. This will\n* automatically find which version of the capability named in the JWT is running\n* on the device of the JWT and get the data for that version.\n*\n* Example usage (with webrtc-video):\n*\n* ```js\n* const { agentStatus, topicData } = useTopics({ jwt, topics: [\n* '/options/videoSource',\n* '/stats/+/log/'\n* ]});\n* ```\n*\n* @param {object} options An object containing:\n* `JWT`: A list of subtopics of the capability named in the JWT.\n* `topics`: A list of subtopics of the capability named in the JWT.\n* @returns {object} An object `{data, mqttSync, ready, agentStatus, topicData}`\n* where:\n* `agentStatus` is the `status` field of the running robot agent, including\n* heartbeat and runningPackages, and\n* `topicData` is the data for the selected topics of the capability\n*/\nexport const useTopics = ({jwt, host = 'transitiverobotics.com', ssl = true,\n topics = [], appReact}) => {\n\n const { useState, useEffect } = appReact || React;\n\n // We need to make sure we don't resubscribe (below) when this function\n // is called with the same content of `topics` but a different object.\n const [topicList, setTopicList] = useState();\n !_.isEqual(topicList, topics) && setTopicList(topics);\n\n const {device, id, capability} = decodeJWT(jwt);\n if (device == '_fleet') {\n log.warn('useTopics only works for device JWTs, not _fleet ones');\n return;\n }\n\n const agentPrefix = `/${id}/${device}/@transitive-robotics/_robot-agent/+/status`;\n\n const {mqttSync, data, status, ready, StatusComponent} =\n useMqttSync({jwt, id, mqttUrl: `ws${ssl ? 's' : ''}://mqtt.${host}`, appReact});\n\n useEffect(() => {\n if (ready) {\n mqttSync.subscribe(agentPrefix, (err) => err && console.warn(err));\n }\n }, [mqttSync, ready]);\n\n const agentStatus = mergeVersions(\n data[id]?.[device]['@transitive-robotics']['_robot-agent'], 'status').status;\n const runningPackages = agentStatus?.runningPackages;\n\n const [scope, capName] = capability.split('/');\n const versions = runningPackages?.[scope]?.[capName];\n const runningVersion = versions && Object.values(versions).filter(Boolean)[0];\n const prefix = `/${id}/${device}/${capability}/${runningVersion}`;\n\n useEffect(() => {\n log.debug('topics', topics);\n if (runningVersion) {\n topics.forEach(topic => {\n log.debug(`subscribing to ${prefix}${topic}`);\n mqttSync.subscribe(`${prefix}${topic}`,\n (err) => err && log.warn(err));\n });\n }\n }, [topicList, runningVersion, mqttSync]);\n\n const topicData = _.get(data, topicToPath(prefix));\n // log.debug(data, agentStatus, topicData);\n\n return {data: data?.[id]?.[device], mqttSync, agentStatus, topicData};\n };\n\n\nconst listeners = {};\nconst loadedModules = {};\n/** Hook to load a Transitive capability. Besides loading the custom element,\n* this hook also returns any functions and objects the component exports in\n* `loadedModule`. Example:\n* ```js\n* const {loaded, loadedModule} = useCapability({\n* capability: '@transitive-robotics/terminal',\n* name: 'mock-device',\n* userId: 'user123',\n* deviceId: 'd_mydevice123',\n* });\n* ```\n*/\nexport const useCapability = ({ capability, name, userId, deviceId,\n host = 'transitiverobotics.com', ssl = true, appReact\n }) => {\n const { useState, useEffect } = appReact || React;\n\n const [returns, setReturns] = useState({ loaded: false });\n\n // called when loaded\n const done = (message, theModule) => {\n log.debug(`custom component ${name}: ${message}`);\n loadedModules[name] = theModule;\n setReturns(x => ({...x, loadedModule: theModule, loaded: !!theModule}));\n };\n\n /** set the returns for all listeners */\n const notifyListeners = (...args) => listeners[name].forEach(l => l(...args));\n\n useEffect(() => {\n log.debug(`loading custom component ${name}`);\n\n if (loadedModules[name]) {\n return done('already loaded', loadedModules[name]);\n }\n if (listeners[name]) {\n log.debug('already loading');\n // get notified when loading completes\n listeners[name].push(done);\n return;\n }\n listeners[name] = [done];\n\n const baseUrl = `http${ssl ? 's' : ''}://portal.${host}`;\n const params = new URLSearchParams({ userId, deviceId });\n // filename without extension as we'll try multiple\n const fileBasename = `${baseUrl}/running/${capability}/dist/${name}`;\n\n /* Since some users use webpack and webpack is stupid, we need to use\n this magic comment for it to ignore these (remote) requests, see:\n https://webpack.js.org/api/module-methods/#webpackignore. */\n import(/* webpackIgnore: true */\n `${fileBasename}.esm.js?${params.toString()}`).then(\n esm => notifyListeners('loaded esm', esm),\n error => {\n log.warn(`No ESM module found for ${name}, loading iife`, error);\n import(/* webpackIgnore: true */\n `${fileBasename}.js?${params.toString()}`).then(\n iife => notifyListeners('loaded iife', iife),\n error => log.error(`Failed to load ${name} iife`, error));\n });\n }, [capability, name, userId, deviceId]);\n\n return returns;\n };\n\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["module", "module", "attribute", "module", "React", "styles", "module", "_", "topicToPath", "pathToTopic", "module", "module", "_", "topicToPath", "pathToTopic", "getLogger", "clone", "decodeJWT", "prefix", "mergeVersions", "module", "_", "topicToPath", "pathToTopic", "module", "module", "_", "topicToPath", "pathToTopic", "getLogger", "mergeVersions", "log", "clone", "MqttSync", "value", "err", "import_react", "MS", "MqttSync", "useState", "useRef", "useEffect", "React", "client", "mqtt", "_", "error", "React", "name", "React", "ReactWebComponent"]
|
|
7
7
|
}
|
package/docs/client.md
CHANGED
|
@@ -158,6 +158,10 @@ parse document cookies
|
|
|
158
158
|
|
|
159
159
|
* `str`  
|
|
160
160
|
|
|
161
|
+
## TimerContext
|
|
162
|
+
|
|
163
|
+
A Timeout component: removes the children once time runs out
|
|
164
|
+
|
|
161
165
|
## TransitiveCapability
|
|
162
166
|
|
|
163
167
|
Dynamically load and use the Transitive web component specified in the JWT.
|
|
@@ -196,6 +200,55 @@ JWT for webrtc-video and use:
|
|
|
196
200
|
* `$0.ssl` (optional, default `true`)
|
|
197
201
|
* `$0.config` **...any** 
|
|
198
202
|
|
|
203
|
+
## TreeSelector
|
|
204
|
+
|
|
205
|
+
takes options in the below format and renders a "tree of dropdowns" to
|
|
206
|
+
allow user to select from these options in sequence.
|
|
207
|
+
Format:
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
{ selector: 'video source',
|
|
211
|
+
field: 'type',
|
|
212
|
+
options: [
|
|
213
|
+
{ label: 'ROS Topic', // label for this option (in parent selector)
|
|
214
|
+
value: 'rostopic' // value to use when selected
|
|
215
|
+
field: 'value', // the field for which options list possible values
|
|
216
|
+
selector: 'ROS Version', // label for next selector
|
|
217
|
+
options: [
|
|
218
|
+
{ label: 'ROS1',
|
|
219
|
+
options: [{
|
|
220
|
+
label: 'topic1',
|
|
221
|
+
value: 'topic1'
|
|
222
|
+
}],
|
|
223
|
+
}, {
|
|
224
|
+
label: 'Free form',
|
|
225
|
+
value: 'free-form',
|
|
226
|
+
selector: 'Enter text',
|
|
227
|
+
field: 'textParam',
|
|
228
|
+
}, {
|
|
229
|
+
label: 'A Number',
|
|
230
|
+
value: 'free-form-number',
|
|
231
|
+
selector: 'Enter number',
|
|
232
|
+
type: 'number',
|
|
233
|
+
field: 'numberParam',
|
|
234
|
+
}, {
|
|
235
|
+
label: 'A Date',
|
|
236
|
+
value: 'free-form-date',
|
|
237
|
+
selector: 'Enter date',
|
|
238
|
+
type: 'datetime-local',
|
|
239
|
+
field: 'dateParam',
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
...
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### Parameters
|
|
249
|
+
|
|
250
|
+
* `props`  
|
|
251
|
+
|
|
199
252
|
## useCapability
|
|
200
253
|
|
|
201
254
|
Hook to load a Transitive capability. Besides loading the custom element,
|