Skip to content

Commit c7182b9

Browse files
Eugene Ostroukhovjasnell
Eugene Ostroukhov
authored andcommitted
inspector: JavaScript bindings for the inspector
PR-URL: #12263 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Sam Roberts <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Josh Gavant <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 3c3b36a commit c7182b9

File tree

8 files changed

+463
-1
lines changed

8 files changed

+463
-1
lines changed

doc/api/_toc.md

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* [Globals](globals.html)
2525
* [HTTP](http.html)
2626
* [HTTPS](https.html)
27+
* [Inspector](inspector.html)
2728
* [Modules](modules.html)
2829
* [Net](net.html)
2930
* [OS](os.html)

doc/api/all.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
@include globals
1919
@include http
2020
@include https
21+
@include inspector
2122
@include modules
2223
@include net
2324
@include os

doc/api/inspector.md

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Inspector
2+
3+
> Stability: 1 - Experimental
4+
5+
The `inspector` module provides an API for interacting with the V8 inspector.
6+
7+
It can be accessed using:
8+
9+
```js
10+
const inspector = require('inspector');
11+
```
12+
13+
## Class: inspector.Session
14+
15+
The `inspector.Session` is used for dispatching messages to the V8 inspector
16+
back-end and receiving message responses and notifications.
17+
18+
### Constructor: new inspector.Session()
19+
<!-- YAML
20+
added: REPLACEME
21+
-->
22+
23+
Create a new instance of the `inspector.Session` class. The inspector session
24+
needs to be connected through [`session.connect()`][] before the messages
25+
can be dispatched to the inspector backend.
26+
27+
`inspector.Session` is an [`EventEmitter`][] with the following events:
28+
29+
### Event: 'inspectorNotification'
30+
<!-- YAML
31+
added: REPLACEME
32+
-->
33+
34+
* {Object} The notification message object
35+
36+
Emitted when any notification from the V8 Inspector is received.
37+
38+
```js
39+
session.on('inspectorNotification', (message) => console.log(message.method));
40+
// Debugger.paused
41+
// Debugger.resumed
42+
```
43+
44+
It is also possible to subscribe only to notifications with specific method:
45+
46+
### Event: &lt;inspector-protocol-method&gt;
47+
<!-- YAML
48+
added: REPLACEME
49+
-->
50+
51+
* {Object} The notification message object
52+
53+
Emitted when an inspector notification is received that has its method field set
54+
to the `<inspector-protocol-method>` value.
55+
56+
The following snippet installs a listener on the [`Debugger.paused`][]
57+
event, and prints the reason for program suspension whenever program
58+
execution is suspended (through breakpoints, for example):
59+
60+
```js
61+
session.on('Debugger.paused', ({params}) => console.log(params.hitBreakpoints));
62+
// [ '/node/test/inspector/test-bindings.js:11:0' ]
63+
```
64+
65+
### session.connect()
66+
<!-- YAML
67+
added: REPLACEME
68+
-->
69+
70+
Connects a session to the inspector back-end. An exception will be thrown
71+
if there is already a connected session established either through the API or by
72+
a front-end connected to the Inspector WebSocket port.
73+
74+
### session.post(method[, params][, callback])
75+
<!-- YAML
76+
added: REPLACEME
77+
-->
78+
79+
* method {string}
80+
* params {Object}
81+
* callback {Function}
82+
83+
Posts a message to the inspector back-end. `callback` will be notified when
84+
a response is received. `callback` is a function that accepts two optional
85+
arguments - error and message-specific result.
86+
87+
```js
88+
session.post('Runtime.evaluate', {'expression': '2 + 2'},
89+
(error, {result}) => console.log(result.value));
90+
// Output: { type: 'number', value: 4, description: '4' }
91+
```
92+
93+
The latest version of the V8 inspector protocol is published on the
94+
[Chrome DevTools Protocol Viewer][].
95+
96+
Node inspector supports all the Chrome DevTools Protocol domains declared
97+
by V8. Chrome DevTools Protocol domain provides an interface for interacting
98+
with one of the runtime agents used to inspect the application state and listen
99+
to the run-time events.
100+
101+
### session.disconnect()
102+
<!-- YAML
103+
added: REPLACEME
104+
-->
105+
106+
Immediately close the session. All pending message callbacks will be called
107+
with an error. [`session.connect()`] will need to be called to be able to send
108+
messages again. Reconnected session will lose all inspector state, such as
109+
enabled agents or configured breakpoints.
110+
111+
[`session.connect()`]: #sessionconnect
112+
[`Debugger.paused`]: https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-paused
113+
[`EventEmitter`]: events.html#events_class_eventemitter
114+
[Chrome DevTools Protocol Viewer]: https://chromedevtools.github.io/devtools-protocol/v8/

lib/inspector.js

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict';
2+
3+
const connect = process.binding('inspector').connect;
4+
const EventEmitter = require('events');
5+
const util = require('util');
6+
7+
if (!connect)
8+
throw new Error('Inspector is not available');
9+
10+
const connectionSymbol = Symbol('connectionProperty');
11+
const messageCallbacksSymbol = Symbol('messageCallbacks');
12+
const nextIdSymbol = Symbol('nextId');
13+
const onMessageSymbol = Symbol('onMessage');
14+
15+
class Session extends EventEmitter {
16+
constructor() {
17+
super();
18+
this[connectionSymbol] = null;
19+
this[nextIdSymbol] = 1;
20+
this[messageCallbacksSymbol] = new Map();
21+
}
22+
23+
connect() {
24+
if (this[connectionSymbol])
25+
throw new Error('Already connected');
26+
this[connectionSymbol] =
27+
connect((message) => this[onMessageSymbol](message));
28+
}
29+
30+
[onMessageSymbol](message) {
31+
const parsed = JSON.parse(message);
32+
if (parsed.id) {
33+
const callback = this[messageCallbacksSymbol].get(parsed.id);
34+
this[messageCallbacksSymbol].delete(parsed.id);
35+
if (callback)
36+
callback(parsed.error || null, parsed.result || null);
37+
} else {
38+
this.emit(parsed.method, parsed);
39+
this.emit('inspectorNotification', parsed);
40+
}
41+
}
42+
43+
post(method, params, callback) {
44+
if (typeof method !== 'string')
45+
throw new TypeError(
46+
`"method" must be a string, got ${typeof method} instead`);
47+
if (!callback && util.isFunction(params)) {
48+
callback = params;
49+
params = null;
50+
}
51+
if (params && typeof params !== 'object')
52+
throw new TypeError(
53+
`"params" must be an object, got ${typeof params} instead`);
54+
if (callback && typeof callback !== 'function')
55+
throw new TypeError(
56+
`"callback" must be a function, got ${typeof callback} instead`);
57+
58+
if (!this[connectionSymbol])
59+
throw new Error('Session is not connected');
60+
const id = this[nextIdSymbol]++;
61+
const message = {id, method};
62+
if (params) {
63+
message['params'] = params;
64+
}
65+
if (callback) {
66+
this[messageCallbacksSymbol].set(id, callback);
67+
}
68+
this[connectionSymbol].dispatch(JSON.stringify(message));
69+
}
70+
71+
disconnect() {
72+
if (!this[connectionSymbol])
73+
return;
74+
this[connectionSymbol].disconnect();
75+
this[connectionSymbol] = null;
76+
const remainingCallbacks = this[messageCallbacksSymbol].values();
77+
for (const callback of remainingCallbacks) {
78+
process.nextTick(callback, new Error('Session was closed'));
79+
}
80+
this[messageCallbacksSymbol].clear();
81+
this[nextIdSymbol] = 1;
82+
}
83+
}
84+
85+
module.exports = {
86+
Session
87+
};

node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
'lib/_http_outgoing.js',
4444
'lib/_http_server.js',
4545
'lib/https.js',
46+
'lib/inspector.js',
4647
'lib/module.js',
4748
'lib/net.js',
4849
'lib/os.js',

0 commit comments

Comments
 (0)