Add inspector tab

This commit is contained in:
Tobias Baunbaek
2024-01-24 14:23:47 +01:00
parent 2b3fc63df6
commit 4a01ee2db6
3 changed files with 197 additions and 32 deletions

View File

@@ -40,14 +40,16 @@
background: #efeaea;
color: #151517;
}
a:visited, a:active, a { color: #B0D944; outline: none; text-decoration: none; }
#logo {
float: left;
margin-right: 2rem;
margin-left: 0.65rem;
margin-top: -0.1rem;
}
nav {
height: 146px;
float: left;
@@ -55,15 +57,20 @@
padding-top: 2em;
margin-right: 30rem;
}
nav > a {
display: block;
}
section {
display: none;
}
section:target {
display: block;
}
pre {
background: none;
color: #B0D944;
@@ -96,29 +103,29 @@
margin-top: .75rem;
user-select: none;
}
h1 { padding: .5rem; border-right: 1px solid #B0D944; border-bottom: 1px solid #B0D944; display: inline-block; padding-right: 0.75em; padding-left: 0.5em; font-weight: bold; font-size: 1.6rem; margin-block-start: 0.67em; margin-block-end: 0.67em; }
blockquote { outline: 1px solid #323532; margin-inline-start: 0; margin-inline-end: 0; display: block; margin-block-start: 1rem; margin-block-end: 0; padding: 1px; font-size: .825rem }
blockquote::before { content: ""; float: left; font-size: 1.625rem; margin-left: 1rem; margin-right: 0.625rem; }
.col { max-width: 63rem; margin: 0 auto; margin-top: 1rem; }
#back, #mode {
width: 32px;
height: 32px;
position: absolute;
right: 0.4rem;
cursor: pointer;
user-select: none;
opacity: .65;
}
width: 32px;
height: 32px;
position: absolute;
right: 0.4rem;
cursor: pointer;
user-select: none;
opacity: .65;
}
#back {
top: 10px;
}
#back {
top: 10px;
}
#mode {
top: 60px;
}
#mode {
top: 60px;
}
#bar {
backdrop-filter: blur(64px);
@@ -154,10 +161,10 @@
app-router[data-load="docs-viewer"] a[href='/documentation'], app-router[data-load="docs-viewer"] a:active[href='/documentation'], app-router[data-load="docs-viewer"] a:visited[href='/documentation'] {
color: rgb(78, 250, 92);
}
</style>
<div id="bar"><pear-ctrl></pear-ctrl></div>
<app-router docs-viewer="/documentation" system-status="/" data-load="system-status">
<app-router docs-viewer="/documentation" system-status="/" data-load="system-status" devtools-inspector="/devtools-inspector">
<header>
<div id="headin">
<svg id="logo" width="102" height="146" viewBox="0 0 102 146" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -187,6 +194,7 @@
<nav>
<a href="/" style="text-indent: -1rem">System Status</a>
<a href="/documentation">Documentation</a>
<a href="/devtools-inspector">Inspector</a>
</nav>
<pre>
__ \ _ \ _` | __|
@@ -196,14 +204,18 @@
</pre>
</div>
</header>
<section id="status">
<div class="col"><system-status></system-status></div>
</section>
<script type='module' src='./lib/system-status.js'></script>
<section id="documentation">
<div class="col"><docs-viewer></docs-viewer></div>
</section>
<script type='module' src='./lib/docs-viewer.js'></script>
<section id="status">
<div class="col"><system-status></system-status></div>
</section>
<script type='module' src='./lib/system-status.js'></script>
<section id="documentation">
<div class="col"><docs-viewer></docs-viewer></div>
</section>
<script type='module' src='./lib/docs-viewer.js'></script>
<section id="devtools-inspector">
<div class="col"><devtools-inspector></devtools-inspector></div>
</section>
<script type='module' src='./lib/devtools-inspector.js'></script>
</app-router>
<script type='module' src='./lib/app-router.js'></script>
</body>

147
lib/devtools-inspector.js Normal file
View File

@@ -0,0 +1,147 @@
/* eslint-env browser */
import http from 'http'
import { WebSocketServer } from 'ws'
import { Client } from 'pear-inspect'
customElements.define('devtools-inspector', class extends HTMLElement {
constructor () {
super()
this.template = document.createElement('template')
this.template.innerHTML = `
<div>
<style>
#add-key {
width: 100%;
}
#add-key-error {
color: red;
}
</style>
<h1>Inspector</h1>
<div>
This acts as a link between chrome://inspect and debugging Pear apps
</div>
<ol>
<li>Run a pear app with "pear dev . --inspect"</li>
<li>The outputted key should be added here</li>
<li>In Chrome, open chrome://inspect and the app should appear under Targets</li>
</ol>
<div>
<form id="add-key-form">
<input id="add-key" type="text" placeholder="Id given when running pear with --inspect"/>
<p id="add-key-error"></p>
</form>
</div>
<h3 id="no-apps">No apps availble. Add a key above, to start debugging.</h3>
<div id="apps"></div>
</div>
`
this.root = this.attachShadow({ mode: 'open' })
this.root.appendChild(this.template.content.cloneNode(true))
this.$addKeyForm = this.root.querySelector('#add-key-form')
this.$addKey = this.root.querySelector('#add-key')
this.$addKeyError = this.root.querySelector('#add-key-error')
this.$apps = this.root.querySelector('#apps')
this.$noApps = this.root.querySelector('#no-apps')
this.apps = new Map()
this.$addKeyForm.addEventListener('submit', e => {
e.preventDefault()
const key = this.$addKey.value
if (key.length === 0) {
this.$addKeyError.textContent = ''
return
}
if (key.length !== 64) {
this.$addKeyError.textContent = 'Key needs to be 64 characters long'
return
}
const sessionId = generateUuid()
this.apps.set(sessionId, { publicKey: key })
this.$addKey.value = ''
this.$addKeyError.textContent = ''
this.renderApps()
})
const httpServer = http.createServer()
const wsServer = new WebSocketServer({ noServer: true })
httpServer.listen(9229, () => console.log('[httpServer] running on port 9229'))
httpServer.on('request', (req, res) => {
if (req.url !== '/json/list') {
res.writeHead(404)
res.end()
return
}
const targets = [...this.apps].map(([sessionId]) => ({
description: 'node.js instance', // `Pear app: ${app.name}`,
devtoolsFrontendUrl: `devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=127.0.0.1:9229/${sessionId}`,
devtoolsFrontendUrlCompat: `devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/${sessionId}`,
faviconUrl: 'https://nodejs.org/static/images/favicons/favicon.ico',
id: sessionId,
title: 'index.js',
type: 'node',
url: `file:///path/to/some/file/index.js`,
webSocketDebuggerUrl: `ws://127.0.0.1:9229/${sessionId}`
}))
res.writeHead(200, {
'Content-Type': 'application/json; charset=UTF-8',
'Cache-Control': 'no-cache',
'Content-Length': JSON.stringify(targets).length
})
res.end(JSON.stringify(targets))
})
httpServer.on('upgrade', (request, socket, head) => {
console.log(`[httpServer] UPGRADE. url=${request.url}`)
const sessionId = request.url.substr(1)
const sessionIdExists = this.apps.has(sessionId)
if (!sessionIdExists) return socket.destroy()
wsServer.handleUpgrade(request, socket, head, ws => wsServer.emit('connection', ws, request))
})
wsServer.on('connection', (devtoolsSocket, request) => {
const sessionId = request.url.substr(1)
const app = this.apps.get(sessionId)
if (!app) return devtoolsSocket.destroy()
const { publicKey } = app
const inspectorClient = new Client({ publicKey: Buffer.from(publicKey, 'hex') })
inspectorClient.on('message', msg => {
devtoolsSocket.send(JSON.stringify(msg))
})
devtoolsSocket.on('message', msg => {
inspectorClient.send(JSON.parse(msg))
})
})
}
renderApps () {
const content = [...this.apps.values()].map(app => `
<div>
<div>Public key: ${app.publicKey}</div>
</div>
`)
this.$apps.innerHTML = content
if (this.apps.size > 0) this.$noApps.remove()
}
})
// Can't use `uuid` module for some reason as it results in a throw with `crypto` when importing
function generateUuid () {
var result, i, j
result = ''
for (j = 0; j < 32; j++) {
if (j == 8 || j == 12 || j == 16 || j == 20)
result = result + '-'
i = Math.floor(Math.random() * 16).toString(16)
result = result + i
}
return result
}

View File

@@ -10,6 +10,12 @@
}
},
"dependencies": {
"redhat-overpass-font": "^1.0.0"
"bare-path": "^2.1.0",
"pear-inspect": "github:holepunchto/pear-inspect#v1.0.1",
"redhat-overpass-font": "^1.0.0",
"ws": "^8.16.0"
},
"optionalDependencies": {
"bufferutil": "^4.0.8"
}
}