chrome-debugging-client
Advanced tools
Comparing version 0.6.0 to 0.6.1
@@ -1,2 +0,5 @@ | ||
import { IDebuggingProtocolClient } from "./types"; | ||
export default function createDebuggingProtocolClient(): IDebuggingProtocolClient; | ||
import { IConnection, IDebuggingProtocolClient } from "./types"; | ||
export default function createDebuggingProtocolClient(connection: IConnection): IDebuggingProtocolClient; | ||
export declare type ProtocolError = Error & { | ||
code: number; | ||
}; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const events_1 = require("events"); | ||
function createDebuggingProtocolClient() { | ||
return new DebuggingProtocol(); | ||
function createDebuggingProtocolClient(connection) { | ||
return new DebuggingProtocol(connection); | ||
} | ||
@@ -10,17 +18,27 @@ exports.default = createDebuggingProtocolClient; | ||
class DebuggingProtocol extends events_1.EventEmitter { | ||
constructor() { | ||
constructor(connection) { | ||
super(); | ||
this.connection = connection; | ||
this.seq = 0; | ||
this.pendingRequests = new Map(); | ||
this.onMessage = this.onMessage.bind(this); | ||
this.onError = this.onError.bind(this); | ||
this.onClose = this.onClose.bind(this); | ||
this.connection.on("message", this.onMessage); | ||
this.connection.on("error", this.onError); | ||
this.connection.on("close", this.onClose); | ||
} | ||
send(method, params) { | ||
return new Promise((resolve, reject) => { | ||
const id = this.seq++; | ||
this.socket.send(JSON.stringify({ id, method, params })); | ||
this.pendingRequests.set(id, { id, method, params, resolve, reject }); | ||
}).then((res) => { | ||
if (res.error) { | ||
throw new ProtocolError(res.error); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const request = this.createRequest(method, params); | ||
try { | ||
const [, response] = yield Promise.all([ | ||
this.sendRequest(request), | ||
this.getResponse(request), | ||
]); | ||
return response; | ||
} | ||
return res.result; | ||
finally { | ||
this.deleteRequest(request); | ||
} | ||
}); | ||
@@ -32,6 +50,5 @@ } | ||
if (msg.id !== undefined) { | ||
const req = this.pendingRequests.get(msg.id); | ||
this.pendingRequests.delete(msg.id); | ||
if (req) { | ||
req.resolve(msg); | ||
const request = this.pendingRequests.get(msg.id); | ||
if (request) { | ||
request.resolve(msg); | ||
} | ||
@@ -45,5 +62,7 @@ } | ||
this.onError(err); | ||
this.socket.close(); | ||
} | ||
} | ||
close() { | ||
return this.connection.close(); | ||
} | ||
onClose() { | ||
@@ -55,5 +74,31 @@ this.clearPending(new Error("socket disconnect")); | ||
this.clearPending(err); | ||
this.socket.close(); | ||
this.emit("error", err); | ||
} | ||
dispose() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
this.connection.removeListener("message", this.onMessage); | ||
this.connection.removeListener("error", this.onError); | ||
this.connection.removeListener("close", this.onClose); | ||
}); | ||
} | ||
createRequest(method, params) { | ||
const req = new CommandRequest(this.seq++, method, params); | ||
this.pendingRequests.set(req.id, req); | ||
return req; | ||
} | ||
getResponse(request) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const response = yield request.response; | ||
if (response.error) { | ||
throw protocolError(response.error); | ||
} | ||
return response.result; | ||
}); | ||
} | ||
sendRequest(req) { | ||
return this.connection.send(JSON.stringify(req)); | ||
} | ||
deleteRequest(req) { | ||
this.pendingRequests.delete(req.id); | ||
} | ||
clearPending(err) { | ||
@@ -68,9 +113,18 @@ if (this.pendingRequests.size) { | ||
} | ||
class ProtocolError extends Error { | ||
constructor(err) { | ||
const msg = err.data ? `${err.message}:${err.data}` : err.message; | ||
super(msg); | ||
this.code = err.code; | ||
class CommandRequest { | ||
constructor(id, method, params) { | ||
this.id = id; | ||
this.method = method; | ||
this.params = params; | ||
this.response = new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
}); | ||
} | ||
} | ||
function protocolError({ message, code, data }) { | ||
const msg = data ? `${message}:${data}` : message; | ||
const err = new Error(msg); | ||
return Object.assign(err, { code }); | ||
} | ||
//# sourceMappingURL=create-debugging-protocol-client.js.map |
import { ISession } from "./types"; | ||
export declare function createSession<T>(cb: (session: ISession) => PromiseLike<T> | T): Promise<T>; | ||
export declare function createSessions<T>(count: number, cb: (sessions: ISession[]) => PromiseLike<T> | T): Promise<T>; | ||
export declare type SessionCallback<T> = (session: ISession) => PromiseLike<T> | T; | ||
export declare function createSession<T>(cb: SessionCallback<T>): Promise<T>; | ||
export declare function createSession(): ISession; |
@@ -14,2 +14,3 @@ "use strict"; | ||
const create_http_client_1 = require("./create-http-client"); | ||
const create_target_connection_1 = require("./create-target-connection"); | ||
const create_tmpdir_1 = require("./create-tmpdir"); | ||
@@ -21,2 +22,9 @@ const disposables_1 = require("./disposables"); | ||
function createSession(cb) { | ||
if (cb === undefined) { | ||
return new Session(); | ||
} | ||
return usingSession(cb); | ||
} | ||
exports.createSession = createSession; | ||
function usingSession(cb) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -32,21 +40,2 @@ const session = new Session(); | ||
} | ||
exports.createSession = createSession; | ||
function createSessions(count, cb) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const disposables = new disposables_1.default(); | ||
const sessions = []; | ||
try { | ||
while (count--) { | ||
const session = new Session(); | ||
disposables.add(session); | ||
sessions.push(session); | ||
} | ||
return yield cb(sessions); | ||
} | ||
finally { | ||
yield disposables.dispose(); | ||
} | ||
}); | ||
} | ||
exports.createSessions = createSessions; | ||
class Session { | ||
@@ -71,12 +60,30 @@ constructor() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const debuggingProtocol = create_debugging_protocol_client_1.default(); | ||
const connection = yield open_web_socket_1.default(webSocketDebuggerUrl, debuggingProtocol); | ||
this.disposables.add(connection); | ||
return debuggingProtocol; | ||
return this.createDebuggingProtocolClient(this.addDisposable(yield open_web_socket_1.default(webSocketDebuggerUrl))); | ||
}); | ||
} | ||
attachToTarget(browserClient, targetId) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const { sessionId } = yield browserClient.send("Target.attachToTarget", { | ||
targetId, | ||
}); | ||
return this.createTargetSessionClient(browserClient, sessionId); | ||
}); | ||
} | ||
createTargetSessionClient(browserClient, sessionId) { | ||
const connection = this.addDisposable(create_target_connection_1.default(browserClient, sessionId)); | ||
return this.createDebuggingProtocolClient(connection); | ||
} | ||
createSession() { | ||
return this.addDisposable(new Session()); | ||
} | ||
dispose() { | ||
return this.disposables.dispose(); | ||
} | ||
createDebuggingProtocolClient(connection) { | ||
return this.addDisposable(create_debugging_protocol_client_1.default(connection)); | ||
} | ||
addDisposable(disposable) { | ||
return disposable; | ||
} | ||
} | ||
//# sourceMappingURL=create-session.js.map |
@@ -1,2 +0,5 @@ | ||
import { ITmpDir } from "./types"; | ||
import { IDisposable } from "./types"; | ||
export interface ITmpDir extends IDisposable { | ||
path: string; | ||
} | ||
export default function createTmpDir(): Promise<ITmpDir>; |
import { IDisposable } from "./types"; | ||
export default class Disposables implements IDisposable { | ||
private disposables; | ||
add(disposable: IDisposable): void; | ||
add<T extends IDisposable>(disposable: T): T; | ||
dispose(): Promise<void>; | ||
} |
@@ -17,2 +17,3 @@ "use strict"; | ||
this.disposables.push(disposable); | ||
return disposable; | ||
} | ||
@@ -22,11 +23,12 @@ dispose() { | ||
const { disposables } = this; | ||
for (let i = disposables.length - 1; i >= 0; i--) { | ||
let disposable; | ||
while ((disposable = disposables.pop()) !== undefined) { | ||
try { | ||
yield disposables[i].dispose(); | ||
disposables.length = i; | ||
yield disposable.dispose(); | ||
} | ||
catch (e) { | ||
/* tslint:disable:no-console */ | ||
console.error(e); | ||
/* tslint:enable:no-console */ | ||
catch (err) { | ||
// intentionally ignored because dispose meant to be called from finally | ||
// don't want to overwrite the error | ||
// tslint:disable-next-line:no-console | ||
console.error(err); | ||
} | ||
@@ -33,0 +35,0 @@ } |
export * from "./types"; | ||
export { default as createAPIClient } from "./create-api-client"; | ||
export { default as createDebuggingProtocolClient } from "./create-debugging-protocol-client"; | ||
export { createSession, createSessions } from "./create-session"; | ||
export { createSession, SessionCallback } from "./create-session"; | ||
export * from "./flags"; |
@@ -6,10 +6,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var create_api_client_1 = require("./create-api-client"); | ||
exports.createAPIClient = create_api_client_1.default; | ||
var create_debugging_protocol_client_1 = require("./create-debugging-protocol-client"); | ||
exports.createDebuggingProtocolClient = create_debugging_protocol_client_1.default; | ||
var create_session_1 = require("./create-session"); | ||
exports.createSession = create_session_1.createSession; | ||
exports.createSessions = create_session_1.createSessions; | ||
__export(require("./flags")); | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import { IWebSocketConnection, IWebSocketDelegate } from "./types"; | ||
export default function openWebSocket(url: string, delegate: IWebSocketDelegate): Promise<IWebSocketConnection>; | ||
import { IConnection } from "./types"; | ||
export default function openWebSocket(url: string): Promise<IConnection>; |
@@ -11,44 +11,70 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const events_1 = require("events"); | ||
const WebSocket = require("ws"); | ||
const event_promise_1 = require("./event-promise"); | ||
function openWebSocket(url, delegate) { | ||
function openWebSocket(url) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const ws = new WebSocket(url); | ||
yield event_promise_1.eventPromise(ws, "open", "error"); | ||
const onMessage = delegate.onMessage.bind(delegate); | ||
const onError = delegate.onError.bind(delegate); | ||
ws.addListener("message", onMessage); | ||
ws.addListener("error", onError); | ||
function removeListeners() { | ||
ws.removeListener("message", onMessage); | ||
ws.removeListener("error", onError); | ||
} | ||
const closed = new Promise(resolveClose => { | ||
const onClose = () => { | ||
ws.removeListener("close", onClose); | ||
resolveClose(); | ||
}; | ||
ws.addListener("close", onClose); | ||
}).then(() => { | ||
removeListeners(); | ||
delegate.onClose(); | ||
return new WebSocketConnection(ws); | ||
}); | ||
} | ||
exports.default = openWebSocket; | ||
class WebSocketConnection extends events_1.EventEmitter { | ||
constructor(ws) { | ||
super(); | ||
this.ws = ws; | ||
ws.on("message", this.onMessage.bind(this)); | ||
ws.on("error", this.onError.bind(this)); | ||
ws.on("close", this.onClose.bind(this)); | ||
} | ||
send(message) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield send(this.ws, message); | ||
}); | ||
function close() { | ||
if (ws.readyState === WebSocket.OPEN) { | ||
ws.close(); | ||
} | ||
close() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (this.ws.readyState === WebSocket.CLOSED) { | ||
return; | ||
} | ||
this.ws.removeAllListeners(); | ||
const closePromise = event_promise_1.eventPromise(this.ws, "close", "error"); | ||
this.ws.close(); | ||
yield closePromise; | ||
}); | ||
} | ||
dispose() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
try { | ||
yield this.close(); | ||
} | ||
catch (err) { | ||
// ignore err since dispose is called in a finally | ||
// tslint:disable-next-line:no-console | ||
console.error(err); | ||
} | ||
}); | ||
} | ||
onMessage(msg) { | ||
this.emit("message", msg); | ||
} | ||
onError(err) { | ||
this.emit("error", err); | ||
} | ||
onClose() { | ||
this.emit("close"); | ||
this.ws.removeAllListeners(); | ||
} | ||
} | ||
function send(ws, data) { | ||
return new Promise((resolve, reject) => ws.send(data, err => { | ||
if (err) { | ||
reject(err); | ||
} | ||
function dispose() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
close(); | ||
yield closed; | ||
}); | ||
else { | ||
resolve(); | ||
} | ||
function send(data) { | ||
ws.send(data); | ||
} | ||
return (delegate.socket = { send, close, dispose }); | ||
}); | ||
})); | ||
} | ||
exports.default = openWebSocket; | ||
//# sourceMappingURL=open-web-socket.js.map |
/** | ||
* The session is a factory for the various debugging tools/clients that disposes them at the end. | ||
* The session is a factory for the various debugging client primitives | ||
* that tracks all of the disposables it creates, so it can ensure they are | ||
* all cleaned up when it is disposed. | ||
* | ||
* It has no other state. | ||
*/ | ||
export interface ISession extends IDisposable { | ||
/** | ||
* Spawn a chrome instance with a temporary user data folder with | ||
* the Chrome Debugging Protocol open on an ephemeral port. | ||
* @param options | ||
*/ | ||
spawnBrowser(options?: IResolveOptions & ISpawnOptions): Promise<IBrowserProcess>; | ||
/** | ||
* Open a DevTools HTTP Client for the specified host and port. | ||
* @param host {string} | ||
* @param port {number} | ||
* @returns {IAPIClient} | ||
*/ | ||
createAPIClient(host: string, port: number): IAPIClient; | ||
openDebuggingProtocol(webSocketDebuggerUrl: string): Promise<IDebuggingProtocolClient>; | ||
/** | ||
* Open a Chrome Debugging Protocol client for the specified web socket url. | ||
* @param webSocketUrl {string} a web socket url. | ||
*/ | ||
openDebuggingProtocol(webSocketUrl: string): Promise<IDebuggingProtocolClient>; | ||
/** | ||
* Attach a Chrome Debugging Protocol client to the specified target id. | ||
* @param browserClient {IDebuggingProtocolClient} Chrome Debugging Protocol client connected to the browser. | ||
* @param targetId {string} the id of the target. | ||
* @returns {IDebuggingProtocolClient} the Chrome Debugging Protocol client for the specified target. | ||
*/ | ||
attachToTarget(browserClient: IDebuggingProtocolClient, targetId: string): Promise<IDebuggingProtocolClient>; | ||
/** | ||
* Create a Chrome Debugging Protocol client for the specified target session id. | ||
* | ||
* Useful for creating a client for an already attached target. | ||
* | ||
* @param browserClient {IDebuggingProtocolClient} Chrome Debugging Protocol client connected to the browser. | ||
* @param sessionId {string} the session id of the target. | ||
* @returns {IDebuggingProtocolClient} the Chrome Debugging Protocol client for the specified target session. | ||
*/ | ||
createTargetSessionClient(browserClient: IDebuggingProtocolClient, sessionId: string): IDebuggingProtocolClient; | ||
/** | ||
* Create nested session within the current. | ||
* | ||
* Everything created within this session will be disposed with the parent, | ||
* but it allows you to dispose of the session earlier than the parent. | ||
* | ||
* @returns {ISession} | ||
*/ | ||
createSession(): ISession; | ||
} | ||
@@ -32,34 +77,15 @@ export interface IAPIClient { | ||
} | ||
export interface IDebuggingProtocolClient extends IEventNotifier, IWebSocketDelegate { | ||
export interface IDebuggingProtocolClient extends IEventNotifier, IDisposable { | ||
send<T>(command: string, params?: any): Promise<T>; | ||
send(command: string, params?: any): Promise<any>; | ||
close(): Promise<void>; | ||
} | ||
export interface ITmpDir extends IDisposable { | ||
path: string; | ||
} | ||
export interface IEventNotifier { | ||
on(event: string, listener: (evt?: any) => void): any; | ||
once(event: string, listener: (evt?: any) => void): any; | ||
removeListener(event: string, listener: (evt?: any) => void): any; | ||
removeAllListeners(event?: string): any; | ||
} | ||
export interface IHTTPClientFactory { | ||
create(host: string, port: number): IHTTPClient; | ||
} | ||
export interface IHTTPClient { | ||
get(path: string): Promise<string>; | ||
} | ||
export interface IWebSocketOpener { | ||
open(url: string, delegate: IWebSocketDelegate): Promise<IWebSocketConnection>; | ||
} | ||
export interface IWebSocketConnection extends IDisposable { | ||
send(data: string): void; | ||
close(): void; | ||
} | ||
export interface IWebSocketDelegate { | ||
socket: IWebSocketConnection; | ||
onMessage(data: string): void; | ||
onError(err: Error): void; | ||
onClose(): void; | ||
} | ||
export interface IDisposable { | ||
/** called in finally should not reject or will mask original error */ | ||
dispose(): Promise<any>; | ||
@@ -87,1 +113,21 @@ } | ||
} | ||
export interface IHTTPClient { | ||
get(path: string): Promise<string>; | ||
} | ||
export declare type ErrorEventHandler = (error: Error) => void; | ||
export declare type MessageEventHandler = (message: string) => void; | ||
export declare type CloseEventHandler = () => void; | ||
export interface IConnection extends IDisposable, IEventNotifier { | ||
send(message: string): Promise<void>; | ||
close(): Promise<void>; | ||
on(event: "message", listener: MessageEventHandler): any; | ||
on(event: "error", listener: ErrorEventHandler): any; | ||
on(event: "close", listener: CloseEventHandler): any; | ||
once(event: "message", listener: MessageEventHandler): any; | ||
once(event: "error", listener: ErrorEventHandler): any; | ||
once(event: "close", listener: CloseEventHandler): any; | ||
removeListener(event: "message", listener: MessageEventHandler): any; | ||
removeListener(event: "error", listener: ErrorEventHandler): any; | ||
removeListener(event: "close", listener: CloseEventHandler): any; | ||
removeAllListeners(event: "message" | "error" | "close"): any; | ||
} |
{ | ||
"name": "chrome-debugging-client", | ||
"version": "0.6.0", | ||
"version": "0.6.1", | ||
"description": "An async/await friendly Chrome debugging client with TypeScript support", | ||
"keywords": [ | ||
"CDP", | ||
"chrome", | ||
@@ -36,2 +37,3 @@ "debugging", | ||
"author": "Kris Selden <kris.selden@gmail.com>", | ||
"license": "BSD-2-Clause", | ||
"repository": { | ||
@@ -41,3 +43,2 @@ "type": "git", | ||
}, | ||
"license": "Apache-2.0", | ||
"dependencies": { | ||
@@ -61,2 +62,4 @@ "@types/node": "^8.9.5", | ||
"tslint": "^5.9.1", | ||
"tslint-config-prettier": "^1.13.0", | ||
"tslint-plugin-prettier": "^1.3.0", | ||
"typescript": "^2.7.2" | ||
@@ -63,0 +66,0 @@ }, |
@@ -1,3 +0,3 @@ | ||
chrome-debugging-client | ||
======================= | ||
# chrome-debugging-client | ||
[![Build Status](https://travis-ci.org/devtrace/chrome-debugging-client.svg?branch=master)](https://travis-ci.org/devtrace/chrome-debugging-client) | ||
@@ -28,4 +28,3 @@ | ||
// and the debugger open to an ephemeral port | ||
const process = await session.spawnBrowser('exact', { | ||
executablePath: '/usr/bin/google-chrome-beta', | ||
const process = await session.spawnBrowser({ | ||
additionalArguments: ['--headless', '--disable-gpu', '--hide-scrollbars', '--mute-audio'], | ||
@@ -69,8 +68,10 @@ windowSize: { width: 640, height: 320 } | ||
## customize browser executable path | ||
By default, this tool launches Chrome Canary. It may error if it cannot find the executable. For this and other reasons, you can configure the executable path like so: | ||
By default, this tool chrome-launcher to find chrome. It may error if it cannot find the executable. For this and other reasons, you can configure via a CHROME_PATH environment variable or pass in the executablePath like so: | ||
```js | ||
// example for macOS | ||
let browser = await session.spawnBrowser('exact', { | ||
let browser = await session.spawnBrowser({ | ||
executablePath: '/Users/someone/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary' | ||
}); | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
893208
69
16603
76
9