Work in progress
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,6 +2,7 @@ node_modules
|
|||||||
coverage
|
coverage
|
||||||
build
|
build
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
.DS_Store
|
||||||
test.dot
|
test.dot
|
||||||
test.png
|
test.png
|
||||||
|
.awcache
|
||||||
|
|||||||
23
app/index.html
Normal file
23
app/index.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Hello React!</title>
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="example"></div>
|
||||||
|
|
||||||
|
<!-- Dependencies -->
|
||||||
|
<script src="./node_modules/react/umd/react.development.js"></script>
|
||||||
|
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||||
|
|
||||||
|
<!-- Main -->
|
||||||
|
<script src="./build/bundle.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6141
app/package-lock.json
generated
Normal file
6141
app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
app/package.json
Normal file
37
app/package.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"name": "mqtt-explorer-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"awesome-typescript-loader": "^5.2.1",
|
||||||
|
"source-map-loader": "^0.2.4",
|
||||||
|
"typescript": "^3.2.2",
|
||||||
|
"webpack": "^4.28.2",
|
||||||
|
"webpack-cli": "^3.1.2",
|
||||||
|
"webpack-dev-server": "^3.1.14"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@material-ui/core": "^3.7.1",
|
||||||
|
"@types/react": "^16.7.18",
|
||||||
|
"@types/react-dom": "^16.0.11",
|
||||||
|
"@types/socket.io-client": "^1.4.32",
|
||||||
|
"@types/vis": "^4.21.9",
|
||||||
|
"cytoscape": "^3.3.1",
|
||||||
|
"cytoscape-dagre": "^2.2.2",
|
||||||
|
"cytoscape-expand-collapse": "^3.1.2",
|
||||||
|
"jquery": "^3.3.1",
|
||||||
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"react": "^16.3.2",
|
||||||
|
"react-cytoscape": "^1.0.6",
|
||||||
|
"react-dom": "^16.3.3",
|
||||||
|
"react-json-view": "^1.19.1",
|
||||||
|
"socket.io-client": "^2.2.0",
|
||||||
|
"vis": "^4.21.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/src/App.tsx
Normal file
37
app/src/App.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as q from '../../src/Model'
|
||||||
|
|
||||||
|
import { AppBar, Toolbar, Typography, InputBase } from '@material-ui/core';
|
||||||
|
|
||||||
|
import { Tree } from "./components/Tree";
|
||||||
|
import { Sidebar } from "./components/Sidebar";
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
|
||||||
|
class State {
|
||||||
|
public selectedNode?: q.TreeNode | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export class App extends React.Component<{}, State> {
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
selectedNode: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return <div>
|
||||||
|
<AppBar>
|
||||||
|
<Toolbar>
|
||||||
|
<Typography variant="h6" color="inherit">MQTT-Xplorer</Typography>
|
||||||
|
</Toolbar>
|
||||||
|
<InputBase />
|
||||||
|
</AppBar>
|
||||||
|
<Tree didSelectNode={(node: q.TreeNode) => {
|
||||||
|
this.setState({selectedNode: node})
|
||||||
|
console.log('did select', node)
|
||||||
|
}}/>
|
||||||
|
// <Sidebar node={this.state.selectedNode} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
74
app/src/components/Sidebar.tsx
Normal file
74
app/src/components/Sidebar.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as q from '../../../src/Model'
|
||||||
|
import Drawer from '@material-ui/core/Drawer';
|
||||||
|
import TextField from '@material-ui/core/TextField';
|
||||||
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import { ValueRenderer } from './ValueRenderer'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
node?: q.TreeNode | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
node?: q.TreeNode | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Sidebar extends React.Component<Props, State> {
|
||||||
|
private updateNode: (node?: q.TreeNode | undefined) => void
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
this.state = {}
|
||||||
|
this.updateNode = (node) => {
|
||||||
|
if (!node) {
|
||||||
|
this.setState(this.state)
|
||||||
|
} else {
|
||||||
|
this.setState({node: node})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getStyle(): {[s: string]: any} {
|
||||||
|
return {
|
||||||
|
marginTop: '64px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillReceiveProps(nextProps: Props) {
|
||||||
|
this.props.node && this.props.node.removeListener('update', this.updateNode)
|
||||||
|
nextProps.node && nextProps.node.on('update', this.updateNode)
|
||||||
|
nextProps.node && this.updateNode(nextProps.node)
|
||||||
|
}
|
||||||
|
|
||||||
|
private open() {
|
||||||
|
return this.props.node !== undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return <Drawer open={this.open()} variant="permanent" anchor="right">
|
||||||
|
{this.renderNode()}
|
||||||
|
</Drawer>
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderNode() {
|
||||||
|
let style: React.CSSProperties = {display: 'block', width: '40vw'}
|
||||||
|
let topicStyle: React.CSSProperties = {width: '100%'}
|
||||||
|
|
||||||
|
if (!this.state.node) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div style={style}>
|
||||||
|
<TextField style={topicStyle}
|
||||||
|
label="Topic"
|
||||||
|
value={this.state.node.path()}
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
/>
|
||||||
|
<Paper>
|
||||||
|
<ValueRenderer node={this.state.node} />
|
||||||
|
</Paper>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
73
app/src/components/Tree.tsx
Normal file
73
app/src/components/Tree.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as io from 'socket.io-client';
|
||||||
|
import * as q from '../../../src/Model'
|
||||||
|
import { TreeNode } from './TreeNode'
|
||||||
|
import List from '@material-ui/core/List';
|
||||||
|
|
||||||
|
var throttle = require('lodash.throttle');
|
||||||
|
|
||||||
|
class TreeState {
|
||||||
|
public tree: q.Tree
|
||||||
|
public msg: any
|
||||||
|
constructor(tree: q.Tree, msg: any) {
|
||||||
|
this.tree = tree
|
||||||
|
this.msg = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TreeNodeProps {
|
||||||
|
didSelectNode?: (node: q.TreeNode) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Tree extends React.Component<TreeNodeProps, TreeState> {
|
||||||
|
private socket: SocketIOClient.Socket
|
||||||
|
private renderDuration: number = 300
|
||||||
|
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
let tree = new q.Tree()
|
||||||
|
this.state = new TreeState(tree, {})
|
||||||
|
this.socket = io('http://localhost:3000');
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
let updateState = throttle((state: any) => {
|
||||||
|
this.setState(state)
|
||||||
|
updateState.cancel()
|
||||||
|
updateState = throttle(() => {
|
||||||
|
this.setState(state)
|
||||||
|
}, Math.max(this.renderDuration * 5, 300), {trailing: true})
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
this.socket.on('message', (msg: any) => {
|
||||||
|
const edges = msg.topic.split('/')
|
||||||
|
const node = q.TreeNodeFactory.fromEdgesAndValue(edges, Buffer.from(msg.payload, "base64").toString())
|
||||||
|
this.state.tree.updateWithNode(node.firstNode())
|
||||||
|
|
||||||
|
updateState({tree: this.state.tree, msg: msg})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.socket.removeAllListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
private getStyle(): {[s: string]: any} {
|
||||||
|
return {
|
||||||
|
marginTop: '64px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return <div {...this.props}>
|
||||||
|
<List style={this.getStyle()}>
|
||||||
|
<TreeNode
|
||||||
|
didSelectNode={this.props.didSelectNode}
|
||||||
|
treeNode={this.state.tree}
|
||||||
|
name="/" collapsed={false}
|
||||||
|
performanceCallback={(ms) => this.renderDuration = ms}
|
||||||
|
/>
|
||||||
|
</List>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
194
app/src/components/TreeNode.tsx
Normal file
194
app/src/components/TreeNode.tsx
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as io from 'socket.io-client';
|
||||||
|
import * as q from '../../../src/Model'
|
||||||
|
import List from '@material-ui/core/List';
|
||||||
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
|
import Collapse from '@material-ui/core/Collapse';
|
||||||
|
|
||||||
|
export interface TreeNodeProps {
|
||||||
|
treeNode: q.TreeNode,
|
||||||
|
name?: string | undefined
|
||||||
|
collapsed?: boolean | undefined
|
||||||
|
performanceCallback?: ((ms: number) => void) | undefined
|
||||||
|
didSelectNode?: (node: q.TreeNode) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TreeNodeState {
|
||||||
|
title: string | undefined,
|
||||||
|
collapsed: boolean,
|
||||||
|
collapsedOverride: boolean | undefined,
|
||||||
|
edgeCount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
let collapseLimit = 0
|
||||||
|
declare var performance: any
|
||||||
|
|
||||||
|
export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
|
||||||
|
private dirty: boolean = true
|
||||||
|
private willUpdateTime: number = performance.now()
|
||||||
|
|
||||||
|
constructor(props: TreeNodeProps, state: TreeNodeState) {
|
||||||
|
super(props, state)
|
||||||
|
|
||||||
|
let edgeCount = Object.keys(props.treeNode.edges).length
|
||||||
|
let collapsed = edgeCount > collapseLimit
|
||||||
|
|
||||||
|
this.props.treeNode.on('update', () => {
|
||||||
|
this.dirty = true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.state = {collapsed, edgeCount: edgeCount, collapsedOverride: props.collapsed, title: props.name}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setState(state: any) {
|
||||||
|
this.dirty = true
|
||||||
|
super.setState(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
public shouldComponentUpdate() {
|
||||||
|
return this.dirty
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate() {
|
||||||
|
this.dirty = false
|
||||||
|
if (this.props.performanceCallback) {
|
||||||
|
let renderTime = performance.now()-this.willUpdateTime
|
||||||
|
this.props.performanceCallback(renderTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUpdate() {
|
||||||
|
if (this.props.performanceCallback) {
|
||||||
|
this.willUpdateTime = performance.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private collapsed() {
|
||||||
|
if (this.state.collapsedOverride !== undefined) {
|
||||||
|
return this.state.collapsedOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.state.collapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderNodes() {
|
||||||
|
const edges = Object.values(this.props.treeNode.edges)
|
||||||
|
const listItemStyle = {
|
||||||
|
padding: '3px 8px 3px 8px'
|
||||||
|
}
|
||||||
|
const listStyle = {
|
||||||
|
padding: '3px 8px 3px 16px'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const listItems = edges
|
||||||
|
.map(edge => edge.node)
|
||||||
|
.map(node => <ListItem style={listItemStyle} button key={node.hash()}>
|
||||||
|
<TreeNode didSelectNode={this.props.didSelectNode} treeNode={node} />
|
||||||
|
</ListItem>)
|
||||||
|
|
||||||
|
return <Collapse in={!this.collapsed()} timeout="auto" unmountOnExit>
|
||||||
|
<List style={listStyle}>{listItems}</List>
|
||||||
|
</Collapse>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderSourceEdge() {
|
||||||
|
const style: React.CSSProperties = {
|
||||||
|
fontWeight: "bold",
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'inline-block',
|
||||||
|
}
|
||||||
|
let name = this.state.title || (this.props.treeNode.sourceEdge && this.props.treeNode.sourceEdge.name)
|
||||||
|
|
||||||
|
return <span style={style} onClick={() => this.toggle()}>{name}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
private getStyle(): React.CSSProperties {
|
||||||
|
return {
|
||||||
|
display: 'block',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillReceiveProps() {
|
||||||
|
let edgeCount = Object.keys(this.props.treeNode.edges).length
|
||||||
|
this.setState({collapsed: edgeCount > collapseLimit, edgeCount: edgeCount})
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderValue() {
|
||||||
|
const style: React.CSSProperties = {
|
||||||
|
width: "15em",
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
padding: '0',
|
||||||
|
paddingLeft: '5px',
|
||||||
|
display: 'inline-block',
|
||||||
|
}
|
||||||
|
return this.props.treeNode.value
|
||||||
|
? <span
|
||||||
|
style={style}
|
||||||
|
onMouseOver={() => this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)}
|
||||||
|
> = {this.props.treeNode.value.toString()}</span>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
private clear() {
|
||||||
|
return <div style={{clear: 'both'}} />
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderTitleLine() {
|
||||||
|
const style = {
|
||||||
|
lineHeight: '1em'
|
||||||
|
}
|
||||||
|
return <div style={style}>{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const nodeStyle: React.CSSProperties = {
|
||||||
|
//marginLeft: '10px',
|
||||||
|
display: 'block',
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div style={this.getStyle()}>
|
||||||
|
{this.renderTitleLine()}
|
||||||
|
<div style={nodeStyle}>
|
||||||
|
{this.clear()}
|
||||||
|
<div style={this.subnodesStyle()}>
|
||||||
|
{this.collapsed() ? null : this.renderNodes()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
private toggle() {
|
||||||
|
this.setState({collapsedOverride: !this.collapsed()})
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderExpander() {
|
||||||
|
if (this.state.edgeCount === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.collapsed()
|
||||||
|
? <span onClick={() => this.toggle()}>▶</span>
|
||||||
|
: <span onClick={() => this.toggle()}>▼</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderCollapsedSubnodes() {
|
||||||
|
if (this.state.edgeCount === 0 || !this.collapsed()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = {
|
||||||
|
color: '#333'
|
||||||
|
}
|
||||||
|
return <span style={style}>({this.props.treeNode.leafes().length} nodes)</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
private subnodesStyle(): React.CSSProperties {
|
||||||
|
return {
|
||||||
|
display: 'block',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
app/src/components/ValueRenderer.tsx
Normal file
67
app/src/components/ValueRenderer.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as q from '../../../src/Model'
|
||||||
|
import ReactJson from 'react-json-view'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
node?: q.TreeNode | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
node?: q.TreeNode | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ValueRenderer extends React.Component<Props, State> {
|
||||||
|
private updateNode: (node?: q.TreeNode | undefined) => void
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
this.state = {}
|
||||||
|
this.updateNode = (node) => {
|
||||||
|
if (!node) {
|
||||||
|
this.setState(this.state)
|
||||||
|
} else {
|
||||||
|
this.setState({node: node})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillReceiveProps(nextProps: Props) {
|
||||||
|
this.props.node && this.props.node.removeListener('update', this.updateNode)
|
||||||
|
nextProps.node && nextProps.node.on('update', this.updateNode)
|
||||||
|
nextProps.node && this.updateNode(nextProps.node)
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
let node = this.props.node
|
||||||
|
if (!node) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let json
|
||||||
|
try {
|
||||||
|
json = JSON.parse(node.value)
|
||||||
|
} catch(error) {
|
||||||
|
return this.renderRawValue(node.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof json === 'string') {
|
||||||
|
return this.renderRawValue(node.value)
|
||||||
|
} else if (typeof json === 'number') {
|
||||||
|
return this.renderRawValue(node.value)
|
||||||
|
} else {
|
||||||
|
return <ReactJson src={json} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderRawValue(value: string) {
|
||||||
|
let style: React.CSSProperties = {
|
||||||
|
wordBreak: 'break-all',
|
||||||
|
width: '100%',
|
||||||
|
overflow: 'scroll',
|
||||||
|
display: 'block',
|
||||||
|
lineHeight: '1.2em',
|
||||||
|
padding: '12px 5px 12px 5px'
|
||||||
|
}
|
||||||
|
|
||||||
|
return <pre><code style={style}>{value}</code></pre>
|
||||||
|
}
|
||||||
|
}
|
||||||
10
app/src/index.tsx
Normal file
10
app/src/index.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as ReactDOM from "react-dom";
|
||||||
|
import { App } from './App'
|
||||||
|
|
||||||
|
declare var document: any
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<App />,
|
||||||
|
document.getElementById("example")
|
||||||
|
);
|
||||||
22
app/tsconfig.json
Normal file
22
app/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": true,
|
||||||
|
"compilerOptions": {
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strict": true,
|
||||||
|
"lib": ["es2017"],
|
||||||
|
"outDir": "./build/",
|
||||||
|
"sourceMap": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src/**/*"
|
||||||
|
],
|
||||||
|
"awesomeTypescriptLoaderOptions": {
|
||||||
|
"useCache": true,
|
||||||
|
"transpileModule": true,
|
||||||
|
"errorsAsWarnings": true
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/webpack.config.js
Normal file
36
app/webpack.config.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
module.exports = {
|
||||||
|
entry: "./src/index.tsx",
|
||||||
|
output: {
|
||||||
|
filename: "bundle.js",
|
||||||
|
path: __dirname + "/build"
|
||||||
|
},
|
||||||
|
|
||||||
|
mode: 'production',
|
||||||
|
|
||||||
|
// Enable sourcemaps for debugging webpack's output.
|
||||||
|
devtool: "source-map",
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
// Add '.ts' and '.tsx' as resolvable extensions.
|
||||||
|
extensions: [".ts", ".tsx", ".js", ".json"]
|
||||||
|
},
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
|
||||||
|
{ test: /\.tsx?$/, loader: "awesome-typescript-loader" },
|
||||||
|
|
||||||
|
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
|
||||||
|
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// When importing a module whose path matches one of the following, just
|
||||||
|
// assume a corresponding global variable exists and use that instead.
|
||||||
|
// This is important because it allows us to avoid bundling all of our
|
||||||
|
// dependencies, which allows browsers to cache those libraries between builds.
|
||||||
|
externals: {
|
||||||
|
// "react": "React",
|
||||||
|
// "react-dom": "ReactDOM"
|
||||||
|
}
|
||||||
|
};
|
||||||
297
package-lock.json
generated
297
package-lock.json
generated
@@ -169,6 +169,28 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/socket.io": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-Ind+4qMNfQ62llyB4IMs1D8znMEBsMKohZBPqfBUIXqLQ9bdtWIbNTBWwtdcBWJKnokMZGcmWOOKslatni5vtA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"accepts": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
|
||||||
|
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
|
||||||
|
"requires": {
|
||||||
|
"mime-types": "~2.1.18",
|
||||||
|
"negotiator": "0.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
|
||||||
|
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
|
||||||
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
@@ -187,6 +209,11 @@
|
|||||||
"sprintf-js": "~1.0.2"
|
"sprintf-js": "~1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"arraybuffer.slice": {
|
||||||
|
"version": "0.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
|
||||||
|
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
|
||||||
|
},
|
||||||
"arrify": {
|
"arrify": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
|
||||||
@@ -228,11 +255,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"backo2": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||||
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||||
},
|
},
|
||||||
|
"base64-arraybuffer": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
|
||||||
|
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
|
||||||
|
},
|
||||||
|
"base64id": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY="
|
||||||
|
},
|
||||||
|
"better-assert": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
|
||||||
|
"requires": {
|
||||||
|
"callsite": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"bl": {
|
"bl": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
||||||
@@ -242,6 +292,11 @@
|
|||||||
"safe-buffer": "^5.1.1"
|
"safe-buffer": "^5.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"blob": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||||
|
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
||||||
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
@@ -276,6 +331,11 @@
|
|||||||
"readable-stream": "> 1.0.0 < 3.0.0"
|
"readable-stream": "> 1.0.0 < 3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"callsite": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
||||||
|
},
|
||||||
"chai": {
|
"chai": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
|
||||||
@@ -356,6 +416,21 @@
|
|||||||
"minimist": "^1.1.0"
|
"minimist": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"component-bind": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
|
||||||
|
},
|
||||||
|
"component-emitter": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
|
||||||
|
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
|
||||||
|
},
|
||||||
|
"component-inherit": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
|
||||||
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@@ -372,6 +447,11 @@
|
|||||||
"typedarray": "^0.0.6"
|
"typedarray": "^0.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||||
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
@@ -394,7 +474,6 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
@@ -432,6 +511,69 @@
|
|||||||
"once": "^1.4.0"
|
"once": "^1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"engine.io": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "1.0.0",
|
||||||
|
"cookie": "0.3.1",
|
||||||
|
"debug": "~3.1.0",
|
||||||
|
"engine.io-parser": "~2.1.0",
|
||||||
|
"ws": "~6.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ws": {
|
||||||
|
"version": "6.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz",
|
||||||
|
"integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==",
|
||||||
|
"requires": {
|
||||||
|
"async-limiter": "~1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engine.io-client": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-q66JBFuQcy7CSlfAz9L3jH+v7DTT3i6ZEadYcVj2pOs8/0uJHLxKX3WBkGTvULJMdz0tUCyJag0aKT/dpXL9BQ==",
|
||||||
|
"requires": {
|
||||||
|
"component-emitter": "1.2.1",
|
||||||
|
"component-inherit": "0.0.3",
|
||||||
|
"debug": "~3.1.0",
|
||||||
|
"engine.io-parser": "~2.1.1",
|
||||||
|
"has-cors": "1.1.0",
|
||||||
|
"indexof": "0.0.1",
|
||||||
|
"parseqs": "0.0.5",
|
||||||
|
"parseuri": "0.0.5",
|
||||||
|
"ws": "~6.1.0",
|
||||||
|
"xmlhttprequest-ssl": "~1.5.4",
|
||||||
|
"yeast": "0.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ws": {
|
||||||
|
"version": "6.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz",
|
||||||
|
"integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==",
|
||||||
|
"requires": {
|
||||||
|
"async-limiter": "~1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engine.io-parser": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
|
||||||
|
"requires": {
|
||||||
|
"after": "0.8.2",
|
||||||
|
"arraybuffer.slice": "~0.0.7",
|
||||||
|
"base64-arraybuffer": "0.1.5",
|
||||||
|
"blob": "0.0.5",
|
||||||
|
"has-binary2": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"es5-ext": {
|
"es5-ext": {
|
||||||
"version": "0.10.46",
|
"version": "0.10.46",
|
||||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
|
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
|
||||||
@@ -585,6 +727,26 @@
|
|||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"has-binary2": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
|
||||||
|
"requires": {
|
||||||
|
"isarray": "2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-cors": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||||
|
},
|
||||||
"has-flag": {
|
"has-flag": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
@@ -607,6 +769,11 @@
|
|||||||
"xtend": "^4.0.0"
|
"xtend": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"indexof": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
|
||||||
|
},
|
||||||
"inflight": {
|
"inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
@@ -737,6 +904,19 @@
|
|||||||
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
|
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.37.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
|
||||||
|
"integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
|
||||||
|
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "~1.37.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
@@ -852,8 +1032,12 @@
|
|||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
"dev": true
|
},
|
||||||
|
"negotiator": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
||||||
|
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
|
||||||
},
|
},
|
||||||
"next-tick": {
|
"next-tick": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -1999,6 +2183,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"object-component": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
|
||||||
|
},
|
||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@@ -2015,6 +2204,22 @@
|
|||||||
"readable-stream": "^2.0.1"
|
"readable-stream": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"parseqs": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
|
||||||
|
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
|
||||||
|
"requires": {
|
||||||
|
"better-assert": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parseuri": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
|
||||||
|
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
|
||||||
|
"requires": {
|
||||||
|
"better-assert": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"path-dirname": {
|
"path-dirname": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
|
||||||
@@ -2122,6 +2327,77 @@
|
|||||||
"crypt": ">= 0.0.1"
|
"crypt": ">= 0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"socket.io": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "~4.1.0",
|
||||||
|
"engine.io": "~3.3.1",
|
||||||
|
"has-binary2": "~1.0.2",
|
||||||
|
"socket.io-adapter": "~1.1.0",
|
||||||
|
"socket.io-client": "2.2.0",
|
||||||
|
"socket.io-parser": "~3.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"socket.io-adapter": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
|
||||||
|
},
|
||||||
|
"socket.io-client": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==",
|
||||||
|
"requires": {
|
||||||
|
"backo2": "1.0.2",
|
||||||
|
"base64-arraybuffer": "0.1.5",
|
||||||
|
"component-bind": "1.0.0",
|
||||||
|
"component-emitter": "1.2.1",
|
||||||
|
"debug": "~3.1.0",
|
||||||
|
"engine.io-client": "~3.3.1",
|
||||||
|
"has-binary2": "~1.0.2",
|
||||||
|
"has-cors": "1.1.0",
|
||||||
|
"indexof": "0.0.1",
|
||||||
|
"object-component": "0.0.3",
|
||||||
|
"parseqs": "0.0.5",
|
||||||
|
"parseuri": "0.0.5",
|
||||||
|
"socket.io-parser": "~3.3.0",
|
||||||
|
"to-array": "0.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"socket.io-parser": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
|
||||||
|
"requires": {
|
||||||
|
"component-emitter": "1.2.1",
|
||||||
|
"debug": "~3.1.0",
|
||||||
|
"isarray": "2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
@@ -2204,6 +2480,11 @@
|
|||||||
"is-negated-glob": "^1.0.0"
|
"is-negated-glob": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"to-array": {
|
||||||
|
"version": "0.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
||||||
|
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
|
||||||
|
},
|
||||||
"to-fast-properties": {
|
"to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
@@ -2338,11 +2619,21 @@
|
|||||||
"ultron": "~1.1.0"
|
"ultron": "~1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"xmlhttprequest-ssl": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
|
||||||
|
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
|
||||||
|
},
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
|
||||||
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
|
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
|
||||||
},
|
},
|
||||||
|
"yeast": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||||
|
},
|
||||||
"yn": {
|
"yn": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
|
||||||
|
|||||||
@@ -35,8 +35,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/sha1": "^1.1.1",
|
"@types/sha1": "^1.1.1",
|
||||||
|
"@types/socket.io": "^2.1.2",
|
||||||
"mqtt": "^2.18.8",
|
"mqtt": "^2.18.8",
|
||||||
"sha1": "^1.1.1",
|
"sha1": "^1.1.1",
|
||||||
|
"socket.io": "^2.2.0",
|
||||||
"tslint": "^5.12.0",
|
"tslint": "^5.12.0",
|
||||||
"typescript": "^3.2.2"
|
"typescript": "^3.2.2"
|
||||||
},
|
},
|
||||||
|
|||||||
66
src/CytoscapeExport.ts
Normal file
66
src/CytoscapeExport.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Tree, TreeNode } from './Model'
|
||||||
|
|
||||||
|
export class CytoscapeExport {
|
||||||
|
public static renderNodeInformation(node: TreeNode): any {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
id: node.sourceEdge.hash(),
|
||||||
|
label: this.renderLabel(node.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static toDot(tree: Tree): string {
|
||||||
|
let i = 1
|
||||||
|
let leaveEdges = Object.values(tree.edges)
|
||||||
|
.map(e => e.node)
|
||||||
|
.map(node => node.leafes())
|
||||||
|
.reduce((a, b) => a.concat(b), [])
|
||||||
|
.map(leave => leave.branch())
|
||||||
|
|
||||||
|
const allEdges: Array<any> = []
|
||||||
|
const nodeInformation: {[s: string]: any} = {}
|
||||||
|
leaveEdges.map(edges => edges.reduce( (prev, current) => {
|
||||||
|
let currentHash = current.sourceEdge.hash()
|
||||||
|
nodeInformation[currentHash] = this.renderNodeInformation(current)
|
||||||
|
if (current && prev) {
|
||||||
|
allEdges.push({
|
||||||
|
data: {
|
||||||
|
id: prev.sourceEdge.hash()+currentHash,
|
||||||
|
source: prev.sourceEdge.hash(),
|
||||||
|
target: currentHash,
|
||||||
|
label: this.renderLabel(current.sourceEdge.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}))
|
||||||
|
|
||||||
|
return JSON.stringify(
|
||||||
|
[this.renderNodeInformation(tree)]
|
||||||
|
.concat(Object.values(nodeInformation))
|
||||||
|
.concat(allEdges), undefined, ' '
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static renderLabel(value: any): string {
|
||||||
|
let str;
|
||||||
|
if(!isNaN(value)) {
|
||||||
|
str = value
|
||||||
|
} else {
|
||||||
|
str = JSON.stringify(value)
|
||||||
|
if(str && str.length > 0) {
|
||||||
|
str = str.slice(1, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!str) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if(str.length > 20) {
|
||||||
|
str = str.slice(0, 20)+'…'
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/DataSource/DataSource.ts
Normal file
10
src/DataSource/DataSource.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
type ReadyCallback = () => void
|
||||||
|
type MessageCallback = (topic: string, payload: Buffer) => void
|
||||||
|
|
||||||
|
interface DataSource {
|
||||||
|
connect({readyCallback, messageCallback}: { readyCallback: ReadyCallback, messageCallback: MessageCallback }): void
|
||||||
|
disconnect(): void
|
||||||
|
topicSeparator: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export { DataSource, ReadyCallback, MessageCallback }
|
||||||
42
src/DataSource/MqttSource.ts
Normal file
42
src/DataSource/MqttSource.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Client, connect as mqttConnect } from 'mqtt'
|
||||||
|
import { DataSource } from './'
|
||||||
|
|
||||||
|
export class MqttSource implements DataSource {
|
||||||
|
private client: Client | undefined
|
||||||
|
private url: string
|
||||||
|
private subscription: string
|
||||||
|
public topicSeparator = '/'
|
||||||
|
|
||||||
|
constructor({url, subscription}: {url: string, subscription: string}) {
|
||||||
|
this.url = url
|
||||||
|
this.subscription = subscription
|
||||||
|
}
|
||||||
|
|
||||||
|
public connect({
|
||||||
|
readyCallback,
|
||||||
|
messageCallback
|
||||||
|
}: {
|
||||||
|
readyCallback: () => void,
|
||||||
|
messageCallback: (topic: string, message: Buffer) => void
|
||||||
|
}) {
|
||||||
|
const client = mqttConnect(this.url)
|
||||||
|
this.client = client
|
||||||
|
|
||||||
|
client.on('connect', () => {
|
||||||
|
readyCallback()
|
||||||
|
client.subscribe(this.subscription, (err: Error) => {
|
||||||
|
if (err) {
|
||||||
|
throw new Error('mqtt connection failed')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on('message', (topic, message) => {
|
||||||
|
messageCallback(topic, message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnect() {
|
||||||
|
this.client && this.client.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/DataSource/index.ts
Normal file
7
src/DataSource/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { DataSource } from './DataSource'
|
||||||
|
import { MqttSource } from './MqttSource'
|
||||||
|
|
||||||
|
export {
|
||||||
|
DataSource,
|
||||||
|
MqttSource,
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ export class DotExport {
|
|||||||
let i = 1
|
let i = 1
|
||||||
let leaveEdges = Object.values(tree.edges)
|
let leaveEdges = Object.values(tree.edges)
|
||||||
.map(e => e.node)
|
.map(e => e.node)
|
||||||
.map(node => node.leaves())
|
.map(node => node.leafes())
|
||||||
.reduce((a, b) => a.concat(b), [])
|
.reduce((a, b) => a.concat(b), [])
|
||||||
.map(leave => leave.branch())
|
.map(leave => leave.branch())
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ export class DotExport {
|
|||||||
let currentHash = current.sourceEdge.hash()
|
let currentHash = current.sourceEdge.hash()
|
||||||
nodeInformation[currentHash] = this.renderNodeInformation(current)
|
nodeInformation[currentHash] = this.renderNodeInformation(current)
|
||||||
if (current && prev) {
|
if (current && prev) {
|
||||||
allEdges.push(`\t${prev.sourceEdge.hash()} -> ${currentHash} [label="${current.sourceEdge.name}"]`)
|
allEdges.push(`\t${prev.sourceEdge.hash()} -> ${currentHash} [label=${this.renderLabel(current.sourceEdge.name)}]`)
|
||||||
}
|
}
|
||||||
return current
|
return current
|
||||||
}))
|
}))
|
||||||
@@ -39,10 +39,8 @@ export class DotExport {
|
|||||||
str = value
|
str = value
|
||||||
} else {
|
} else {
|
||||||
str = JSON.stringify(value)
|
str = JSON.stringify(value)
|
||||||
console.log(str)
|
|
||||||
if(str && str.length > 0) {
|
if(str && str.length > 0) {
|
||||||
str = str.slice(1, -1)
|
str = str.slice(1, -1)
|
||||||
console.log(str)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,4 @@ export class Edge {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public merge(edge: Edge) {
|
|
||||||
// if (this.node && edge.node) {
|
|
||||||
// this.node.updateWithNode(edge.node)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,43 @@
|
|||||||
import { Edge } from './'
|
import { Edge } from './'
|
||||||
|
import { EventEmitter } from 'events'
|
||||||
|
|
||||||
export class TreeNode {
|
export class TreeNode extends EventEmitter {
|
||||||
public sourceEdge: Edge
|
public sourceEdge?: Edge
|
||||||
public value: any | null | undefined = undefined
|
public value: any | null | undefined = undefined
|
||||||
public edges: {[s: string]: Edge} = {}
|
public edges: {[s: string]: Edge} = {}
|
||||||
|
public collapsed = false
|
||||||
|
|
||||||
constructor(sourceEdge: Edge, value: any) {
|
constructor(sourceEdge: Edge, value: any) {
|
||||||
|
super()
|
||||||
|
|
||||||
this.sourceEdge = sourceEdge
|
this.sourceEdge = sourceEdge
|
||||||
sourceEdge.target = this
|
sourceEdge.target = this
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hash(): string {
|
||||||
|
return 'N' + (this.sourceEdge ? this.sourceEdge.hash() : '')
|
||||||
|
}
|
||||||
|
|
||||||
public firstNode(): TreeNode {
|
public firstNode(): TreeNode {
|
||||||
return this.sourceEdge.firstEdge().node
|
return this.sourceEdge ? this.sourceEdge.firstEdge().node : this
|
||||||
|
}
|
||||||
|
|
||||||
|
public path(): string {
|
||||||
|
return this.branch()
|
||||||
|
.map(node => (node.sourceEdge && node.sourceEdge.name))
|
||||||
|
.filter(name => name !== undefined)
|
||||||
|
.join('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
private previous(): TreeNode | undefined {
|
private previous(): TreeNode | undefined {
|
||||||
return this.sourceEdge.source || undefined
|
return this.sourceEdge ? this.sourceEdge.source || undefined : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
public addEdge(edge: Edge) {
|
public addEdge(edge: Edge) {
|
||||||
this.edges[edge.name] = edge
|
this.edges[edge.name] = edge
|
||||||
edge.source = this
|
edge.source = this
|
||||||
|
this.emit('update')
|
||||||
}
|
}
|
||||||
|
|
||||||
public branch(): Array<TreeNode> {
|
public branch(): Array<TreeNode> {
|
||||||
@@ -34,20 +50,20 @@ export class TreeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public updateWithNode(node: TreeNode) {
|
public updateWithNode(node: TreeNode) {
|
||||||
debugger
|
|
||||||
if (node.value !== undefined) {
|
if (node.value !== undefined) {
|
||||||
this.value = node.value
|
this.value = node.value
|
||||||
}
|
}
|
||||||
this.mergeEdges(node)
|
this.mergeEdges(node)
|
||||||
|
this.emit('update')
|
||||||
}
|
}
|
||||||
|
|
||||||
public leaves(): Array<TreeNode> {
|
public leafes(): Array<TreeNode> {
|
||||||
if (Object.values(this.edges).length === 0) {
|
if (Object.values(this.edges).length === 0) {
|
||||||
return [this]
|
return [this]
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.values(this.edges)
|
return Object.values(this.edges)
|
||||||
.map(e => e.node.leaves())
|
.map(e => e.node.leafes())
|
||||||
.reduce((a, b) => a.concat(b), [])
|
.reduce((a, b) => a.concat(b), [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
62
src/VisExport.ts
Normal file
62
src/VisExport.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { Tree, TreeNode } from './Model'
|
||||||
|
|
||||||
|
export class VisExport {
|
||||||
|
public static renderNodeInformation(node: TreeNode): any {
|
||||||
|
return {
|
||||||
|
id: node.sourceEdge.hash(),
|
||||||
|
label: this.renderLabel(node.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static toDot(tree: Tree): string {
|
||||||
|
let i = 1
|
||||||
|
let leaveEdges = Object.values(tree.edges)
|
||||||
|
.map(e => e.node)
|
||||||
|
.map(node => node.leafes())
|
||||||
|
.reduce((a, b) => a.concat(b), [])
|
||||||
|
.map(leave => leave.branch())
|
||||||
|
|
||||||
|
const allEdges: {[s: string]: any} = {}
|
||||||
|
const nodeInformation: {[s: string]: any} = {}
|
||||||
|
leaveEdges.map(edges => edges.reduce( (prev, current) => {
|
||||||
|
let currentHash = current.sourceEdge.hash()
|
||||||
|
nodeInformation[currentHash] = this.renderNodeInformation(current)
|
||||||
|
if (current && prev) {
|
||||||
|
let edgeId = prev.sourceEdge.hash()+currentHash
|
||||||
|
allEdges[prev.sourceEdge.hash()+currentHash] = {
|
||||||
|
id: prev.sourceEdge.hash()+currentHash,
|
||||||
|
from: prev.sourceEdge.hash(),
|
||||||
|
to: currentHash,
|
||||||
|
label: this.renderLabel(current.sourceEdge.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}))
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
nodes: [this.renderNodeInformation(tree)].concat(Object.values(nodeInformation)),
|
||||||
|
edges: Object.values(allEdges)
|
||||||
|
}, undefined, ' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static renderLabel(value: any): string {
|
||||||
|
let str;
|
||||||
|
if(!isNaN(value)) {
|
||||||
|
str = value
|
||||||
|
} else {
|
||||||
|
str = JSON.stringify(value)
|
||||||
|
if(str && str.length > 0) {
|
||||||
|
str = str.slice(1, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!str) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if(str.length > 20) {
|
||||||
|
str = str.slice(0, 20)+'…'
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/index.ts
73
src/index.ts
@@ -1,41 +1,62 @@
|
|||||||
import { connect as mqttConnect } from 'mqtt'
|
|
||||||
import { TopicProperties, Tree, TreeNodeFactory } from './Model'
|
import { TopicProperties, Tree, TreeNodeFactory } from './Model'
|
||||||
|
import { MqttSource, DataSource } from './DataSource'
|
||||||
import { DotExport } from './DotExport'
|
import { DotExport } from './DotExport'
|
||||||
|
// import { CytoscapeExport } from './CytoscapeExport'
|
||||||
|
// import { VisExport } from './VisExport'
|
||||||
import { writeFileSync } from 'fs'
|
import { writeFileSync } from 'fs'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
|
import * as socketIO from 'socket.io'
|
||||||
|
|
||||||
var client = mqttConnect('mqtt://test.mosquitto.org')
|
const server = require('http').createServer();
|
||||||
const topicSeparator = '/'
|
|
||||||
|
|
||||||
client.on('connect', function () {
|
|
||||||
console.log('connected')
|
|
||||||
client.subscribe('#', (err: Error) => {
|
|
||||||
if (!err) {}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
let tree = new Tree()
|
let tree = new Tree()
|
||||||
|
let dataSource: DataSource = new MqttSource({url: 'mqtt://iot.eclipse.org', subscription: '#'})
|
||||||
|
let count = 200
|
||||||
|
|
||||||
client.on('message', function (topic, message) {
|
const a: Array<any> = []
|
||||||
// message is Buffer
|
|
||||||
const edges = topic.split(topicSeparator)
|
const io = socketIO(server)
|
||||||
let value = message.toString()
|
io.on('connection', client => {
|
||||||
let node = TreeNodeFactory.fromEdgesAndValue(edges, value)
|
console.log('connection')
|
||||||
tree.updateWithNode(node.firstNode())
|
a.forEach(b => {
|
||||||
|
io.emit('message', b)
|
||||||
})
|
})
|
||||||
|
client.on('event', data => { /* … */ });
|
||||||
|
client.on('disconnect', () => { /* … */ });
|
||||||
|
});
|
||||||
|
server.listen(3000);
|
||||||
|
|
||||||
function writeTree() {
|
|
||||||
writeFileSync('./test.dot', DotExport.toDot(tree))
|
dataSource.connect({
|
||||||
let p = spawn('dot', '-Tpng test.dot -o test.png'.split(' '))
|
readyCallback: () => {
|
||||||
|
console.log('connected')
|
||||||
|
},
|
||||||
|
messageCallback: (topic, payload) => {
|
||||||
|
// a.push({topic, payload})
|
||||||
|
if (payload.length > 10000) {
|
||||||
|
payload = payload.slice(0, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(() => {
|
io.emit('message', {topic, payload: payload.toString('base64')})
|
||||||
writeTree()
|
// console.log(topic)
|
||||||
console.log(tree)
|
const edges = topic.split('/')
|
||||||
}, 2000)
|
let value = payload.toString()
|
||||||
|
let node = TreeNodeFactory.fromEdgesAndValue(edges, value)
|
||||||
|
tree.updateWithNode(node.firstNode())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// function writeTree() {
|
||||||
|
// // writeFileSync('./test.dot', DotExport.toDot(tree))
|
||||||
|
// // writeFileSync('./test.json', CytoscapeExport.toDot(tree))
|
||||||
|
// // writeFileSync('./vis.json', VisExport.toDot(tree))
|
||||||
|
// // let p = spawn('dot', '-Tpng test.dot -o test2.png'.split(' '))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// setInterval(() => {
|
||||||
|
// writeTree()
|
||||||
|
// }, 2000)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log(tree)
|
dataSource.disconnect()
|
||||||
|
|
||||||
client.end()
|
|
||||||
}, 1000000)
|
}, 1000000)
|
||||||
|
|||||||
@@ -7,5 +7,13 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"lib": ["es2017"],
|
"lib": ["es2017"],
|
||||||
"sourceMap": true
|
"sourceMap": true
|
||||||
}
|
},
|
||||||
|
"includes": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"app",
|
||||||
|
"node_modules",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user