This commit is contained in:
pelikhan 2021-11-12 22:09:54 -08:00
Родитель 33e059a2d5
Коммит 3e1e5c7779
9 изменённых файлов: 251 добавлений и 5 удалений

1
.github/workflows/build.yml поставляемый
Просмотреть файл

@ -28,6 +28,7 @@ jobs:
- run: yarn prettier
- run: yarn lint
- run: yarn build
- run: yarn build:web
- run: npx semantic-release
if: ${{ github.ref == 'refs/heads/main' }}
env:

Просмотреть файл

@ -7,7 +7,9 @@
"scripts": {
"lint": "eslint src/**/*.ts",
"prettier": "prettier --write src/**/*.ts",
"build": "microbundle",
"build:cli": "microbundle",
"build:web": "tsc public/index.ts",
"devtools": "yarn build:web && node ./dist/cli.js devtools --trace",
"watch": "microbundle watch"
},
"repository": {
@ -41,14 +43,20 @@
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1",
"faye-websocket": "^0.11.4",
"microbundle": "^0.14.1",
"prettier": "^2.4.1",
"semantic-release": "^18.0.0",
"tslint-microsoft-contrib": "^6.2.0"
"tslint-microsoft-contrib": "^6.2.0",
"typescript": "^4.4.4"
},
"optionalDependencies": {
"serialport": "^9.2.5",
"webusb": "^2.2.0",
"ws": "^8.2.3"
}
},
"files": [
"dist",
"public"
]
}

1
public/favicon.svg Normal file
Просмотреть файл

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" xmlns="http://www.w3.org/2000/svg"><path fill="gold" d="M22.369 14.045H4.037a2.618 2.618 0 0 1-2.656.173A2.493 2.493 0 0 1 0 12c0-.933.533-1.788 1.381-2.218a2.618 2.618 0 0 1 2.656.173h18.332c.9 0 1.631.712 1.631 1.59v.91c0 .878-.73 1.59-1.631 1.59zM4.124 12c0-.404-.164-.791-.457-1.077a1.581 1.581 0 0 0-1.104-.446c-.414 0-.811.16-1.104.446A1.504 1.504 0 0 0 1.002 12c0 .404.164.791.457 1.077.293.285.69.446 1.104.446.414 0 .811-.16 1.104-.446.293-.286.457-.673.457-1.077zM4.187 18c0-.404-.167-.791-.464-1.077a1.618 1.618 0 0 0-1.121-.446c-.42 0-.824.16-1.12.446A1.493 1.493 0 0 0 1.016 18c0 .404.167.791.464 1.077.297.285.7.446 1.121.446.42 0 .823-.16 1.12-.446.298-.286.465-.673.465-1.077zm16.157 2.045H4.098a2.692 2.692 0 0 1-2.695.173C.54 19.788 0 18.933 0 18s.54-1.788 1.403-2.218a2.692 2.692 0 0 1 2.695.173h16.246c.915 0 1.656.712 1.656 1.59v.91c0 .878-.741 1.59-1.656 1.59zM24 6.455c0 .878-.73 1.59-1.631 1.59H4.037a2.618 2.618 0 0 1-2.656.173A2.493 2.493 0 0 1 0 6c0-.933.533-1.788 1.381-2.218a2.618 2.618 0 0 1 2.65.168l.006.005h18.332c.9 0 1.631.712 1.631 1.59ZM4.124 6c0-.404-.164-.791-.457-1.077a1.581 1.581 0 0 0-1.104-.446c-.414 0-.811.16-1.104.446A1.504 1.504 0 0 0 1.002 6c0 .404.164.791.457 1.077.293.285.69.446 1.104.446.414 0 .811-.16 1.104-.446.293-.286.457-.673.457-1.077z"/></svg>

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

19
public/index.html Normal file
Просмотреть файл

@ -0,0 +1,19 @@
<html lang="en">
<head>
<link rel="icon" href="/favicon.svg" type="image/x-icon">
</head>
<style>
iframe {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
border: none;
}
</style>
<body>
<iframe id="frame" alt="Jacdac Dashboard" allow="usb;serial;bluetooth;vr" allowfullscreen sandbox="allow-scripts allow-downloads allow-same-origin"></iframe>
<script src="./index.js"></script>
</body>
</html>

32
public/index.js Normal file
Просмотреть файл

@ -0,0 +1,32 @@
(function () {
var frame = document.getElementById("frame");
var sender = Math.random() + "";
frame.src = "https://microsoft.github.io/jacdac-docs/dashboard/#" + sender;
// node.js -> iframe dashboard
var ws = new WebSocket("ws://localhost:8081/");
console.debug("devtools: connecting to local server...");
ws.addEventListener("open", function () {
console.debug("devtools: connected to local server");
});
ws.addEventListener("message", function (msg) {
var data = msg.data;
frame.contentWindow.postMessage({
type: "messagepacket",
channel: "jacdac",
data: data,
sender: sender,
broadcast: true
});
});
ws.addEventListener("close", function () {
console.debug("devtools: connection closed");
});
// iframe dashboard -> node.js
window.addEventListener("message", function (msg) {
var data = msg.data;
if (data && data.type === "messagepacket" && data.channel === "jacdac") {
if ((ws === null || ws === void 0 ? void 0 : ws.readyState) === WebSocket.OPEN)
ws.send(data.data);
}
});
})();

33
public/index.ts Normal file
Просмотреть файл

@ -0,0 +1,33 @@
(function () {
const frame = document.getElementById("frame") as HTMLIFrameElement
const sender = Math.random() + ""
frame.src = "https://microsoft.github.io/jacdac-docs/dashboard/#" + sender
// node.js -> iframe dashboard
const ws = new WebSocket("ws://localhost:8081/")
console.debug(`devtools: connecting to local server...`)
ws.addEventListener("open", () => {
console.debug(`devtools: connected to local server`)
})
ws.addEventListener("message", (msg) => {
const data = msg.data
frame.contentWindow.postMessage({
type: "messagepacket",
channel: "jacdac",
data: data,
sender,
broadcast: true,
})
})
ws.addEventListener("close", () => {
console.debug(`devtools: connection closed`)
})
// iframe dashboard -> node.js
window.addEventListener("message", msg => {
const data = msg.data
if (data && data.type ==="messagepacket" && data.channel === "jacdac") {
if (ws?.readyState === WebSocket.OPEN)
ws.send(data.data)
}
})
})()

117
src/devtools.ts Normal file
Просмотреть файл

@ -0,0 +1,117 @@
import {
JDBus,
Packet,
PACKET_PROCESS,
printPacket,
serializeToTrace,
} from "jacdac-ts"
/* eslint-disable @typescript-eslint/no-var-requires */
const WebSocket = require("faye-websocket")
const http = require("http")
const fs = require("fs")
const url = require("url")
const path = require("path")
const log = console.log
const debug = console.debug
const error = console.error
export async function devToolsCommand(options?: { packets?: boolean }) {
const { packets } = options || {}
const port = 8081
debug(`starting dev tools...`)
log(` dashboard: http://localhost:${port}`)
log(` websocket: ws://localhost:${port}`)
// start http server
const clients: WebSocket[] = []
const server = http.createServer(function (req, res) {
//debug(`${req.method} ${req.url}`)
// parse URL
const parsedUrl = url.parse(req.url)
// extract URL path
let pathname = `.${parsedUrl.pathname}`
if (pathname === "./") pathname = "./index.html"
// based on the URL path, extract the file extension. e.g. .js, .doc, ...
const ext = path.parse(pathname).ext
// maps file extension to MIME typere
const map = {
".ico": "image/x-icon",
".html": "text/html",
".js": "text/javascript",
}
const fname = path.join(__dirname, "../public", pathname)
fs.exists(fname, exist => {
if (!exist) {
// if the file is not found, return 404
res.statusCode = 404
debug(`not found`)
return
}
// if is a directory search for index file matching the extension
//if (fs.statSync(fname).isDirectory()) fname += "index" + ext
// read file from file system
fs.readFile(fname, (err, data) => {
if (err) {
res.statusCode = 500
debug(`error reading file`)
} else {
// if the file is found, set Content-type and send data
res.setHeader("Content-type", map[ext] || "text/plain")
res.end(data)
}
})
})
})
// passive bus to sniff packets
const bus = new JDBus([], { client: false, disableRoleManager: true })
bus.passive = true
const processPacket = (message: ArrayBuffer, sender: string) => {
const data = new Uint8Array(message)
const pkt = Packet.fromBinary(data, bus.timestamp)
pkt.sender = sender
bus.processPacket(pkt)
}
server.on("upgrade", (request, socket, body) => {
// is this a socket?
if (WebSocket.isWebSocket(request)) {
let client = new WebSocket(request, socket, body)
const sender = Math.random() + ""
clients.push(client)
log(`client: connected (${clients.length} clients)`)
client.on("message", event => {
const { data } = event
clients.filter(c => c !== client).forEach(c => c.send(data))
processPacket(data, sender)
})
client.on("close", () => {
const i = clients.indexOf(client)
clients.splice(i, 1)
client = undefined
log(`client: disconnected (${clients.length} clients)`)
})
client.on("error", ev => error(ev))
}
})
if (packets)
bus.on(PACKET_PROCESS, (pkt: Packet) => {
const str = printPacket(pkt, {
showTime: true,
skipRepeatedAnnounce: true,
skipResetIn: true,
})
if (str) debug(serializeToTrace(pkt, 0))
})
bus.start()
server.listen(port)
}

Просмотреть файл

@ -1,6 +1,7 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { program } = require("commander")
import type { CommandOptions } from "commander"
import { devToolsCommand } from "./devtools"
import { parseCommand } from "./parse"
import { streamCommand } from "./stream"
@ -33,13 +34,16 @@ async function mainCli() {
.option("--catalog", "generate .json files for device catalog")
.action(streamCommand)
createCommand("devtools")
.option("-p, --packets", "show all packets")
.action(devToolsCommand)
await program.parseAsync(process.argv)
}
async function mainWrapper() {
try {
await mainCli()
process.exit(0)
} catch (e) {
error("Exception: " + e.stack)
error("Build failed")

Просмотреть файл

@ -3218,6 +3218,13 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
faye-websocket@^0.11.4:
version "0.11.4"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da"
integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==
dependencies:
websocket-driver ">=0.5.1"
figures@^1.0.1:
version "1.7.0"
resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz"
@ -3666,6 +3673,11 @@ http-cache-semantics@^4.1.0:
resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
http-parser-js@>=0.5.1:
version "0.5.3"
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
http-proxy-agent@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz"
@ -6166,7 +6178,7 @@ sade@^1.7.4:
dependencies:
mri "^1.1.0"
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@ -6913,6 +6925,11 @@ typescript@^4.1.3:
resolved "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz"
integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
typescript@^4.4.4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"
integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
uglify-js@^3.1.4:
version "3.14.3"
resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz"
@ -7073,6 +7090,20 @@ webidl-conversions@^3.0.0:
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
websocket-driver@>=0.5.1:
version "0.7.4"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
dependencies:
http-parser-js ">=0.5.1"
safe-buffer ">=5.1.0"
websocket-extensions ">=0.1.1"
websocket-extensions@>=0.1.1:
version "0.1.4"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
webusb@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/webusb/-/webusb-2.2.0.tgz"