forked from microsoft/devicescript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdevtools.ts
More file actions
154 lines (141 loc) · 5.32 KB
/
devtools.ts
File metadata and controls
154 lines (141 loc) · 5.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const SENDER_FIELD = "__jacdac_sender"
/* eslint-disable @typescript-eslint/no-var-requires */
const WebSocket = require("faye-websocket")
import http from "http"
import https from "https"
import url from "url"
import net from "net"
import { CmdOptions, debug, error, log } from "./command"
import { readFileSync, readJSONSync, watch } from "fs-extra"
import { prettySize } from "@devicescript/compiler"
const dasboardPath = "tools/devicescript-devtools"
function fetchProxy(localhost: boolean): Promise<string> {
const protocol = localhost ? http : https
const url = localhost
? "http://localhost:8000/devtools/proxy.html"
: "https://microsoft.github.io/jacdac-docs/devtools/proxy"
//debug(`fetch jacdac devtools proxy at ${url}`)
return new Promise<string>((resolve, reject) => {
protocol
.get(url, res => {
if (res.statusCode != 200)
reject(
new Error(`proxy download failed (${res.statusCode})`)
)
res.setEncoding("utf8")
let body = ""
res.on("data", data => (body += data))
res.on("end", () => {
body = body.replace(
/https:\/\/microsoft.github.io\/jacdac-docs\/dashboard/g,
localhost
? `http://localhost:8000/${dasboardPath}/`
: `https://microsoft.github.io/jacdac-docs/${dasboardPath}/`
)
resolve(body)
})
res.on("error", reject)
})
.on("error", reject)
})
}
export interface DevToolsOptions {
internet?: boolean
localhost?: boolean
bytecodeFile?: string
debugFile?: string
}
export async function devtools(options: DevToolsOptions & CmdOptions) {
const { internet, localhost, bytecodeFile, debugFile } = options
const port = 8081
const tcpPort = 8082
const listenHost = internet ? undefined : "127.0.0.1"
log(`starting dev tools at http://localhost:${port}`)
log(` websocket: ws://localhost:${port}`)
log(` tcpsocket: tcp://localhost:${tcpPort}`)
// download proxy sources
const proxyHtml = await fetchProxy(localhost)
// start http server
const clients: WebSocket[] = []
// upload DeviceScript file is needed
const sendDeviceScript = bytecodeFile
? () => {
const bytecode = readFileSync(bytecodeFile)
const dbg = debugFile ? readJSONSync(debugFile) : undefined
debug(
`refresh bytecode ${prettySize(bytecode.length)}...`
)
const msg = JSON.stringify({
type: "bytecode",
channel: "devicescript",
bytecode: bytecode.toString("hex"),
dbg,
})
clients.forEach(c => c.send(msg))
}
: undefined
const server = http.createServer(function (req, res) {
const parsedUrl = url.parse(req.url)
const pathname = parsedUrl.pathname
if (pathname === "/") {
res.setHeader("Cache-control", "no-cache")
res.setHeader("Content-type", "text/html")
res.end(proxyHtml)
} else {
res.statusCode = 404
}
})
function removeClient(client: WebSocket) {
const i = clients.indexOf(client)
clients.splice(i, 1)
log(`client: disconnected (${clients.length} clients)`)
}
server.on("upgrade", (request, socket, body) => {
// is this a socket?
if (WebSocket.isWebSocket(request)) {
const client = new WebSocket(request, socket, body)
const sender = "ws" + Math.random()
let firstDeviceScript = false
// store sender id to deduped packet
client[SENDER_FIELD] = sender
clients.push(client)
log(`webclient: connected (${sender}, ${clients.length} clients)`)
client.on("message", (event: any) => {
const { data } = event
if (!firstDeviceScript && sendDeviceScript) {
firstDeviceScript = true
sendDeviceScript()
}
})
client.on("close", () => removeClient(client))
client.on("error", (ev: Error) => error(ev))
}
})
const tcpServer = net.createServer((client: any) => {
const sender = "tcp" + Math.random()
client[SENDER_FIELD] = sender
client.send = (pkt0: Buffer) => {
const pkt = new Uint8Array(pkt0)
const b = new Uint8Array(1 + pkt.length)
b[0] = pkt.length
b.set(pkt, 1)
try {
client.write(b)
} catch {
try {
client.end()
} catch {} // eslint-disable-line no-empty
}
}
clients.push(client)
log(`tcpclient: connected (${sender} ${clients.length} clients)`)
client.on("end", () => removeClient(client))
client.on("error", (ev: Error) => error(ev))
})
server.listen(port, listenHost)
tcpServer.listen(tcpPort, listenHost)
if (bytecodeFile) {
debug(`watching ${bytecodeFile}...`)
watch(bytecodeFile, sendDeviceScript)
}
}