@browserless.io/browserless 2.8.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -1
- package/README.md +41 -3
- package/assets/debugger.png +0 -0
- package/bin/scaffold/src/hello-world.http.ts +3 -2
- package/build/browserless.d.ts +5 -4
- package/build/browserless.js +19 -13
- package/build/browsers/chrome.cdp.d.ts +2 -2
- package/build/browsers/chrome.cdp.js +2 -2
- package/build/browsers/chrome.playwright.d.ts +2 -2
- package/build/browsers/chrome.playwright.js +2 -2
- package/build/browsers/chromium.cdp.d.ts +4 -4
- package/build/browsers/chromium.cdp.js +49 -32
- package/build/browsers/chromium.playwright.d.ts +4 -4
- package/build/browsers/chromium.playwright.js +14 -13
- package/build/browsers/firefox.playwright.d.ts +4 -4
- package/build/browsers/firefox.playwright.js +14 -13
- package/build/browsers/index.d.ts +24 -8
- package/build/browsers/index.js +20 -15
- package/build/browsers/webkit.playwright.d.ts +4 -4
- package/build/browsers/webkit.playwright.js +14 -13
- package/build/config.d.ts +3 -0
- package/build/config.js +3 -0
- package/build/file-system.d.ts +2 -3
- package/build/file-system.js +2 -2
- package/build/http.d.ts +1 -0
- package/build/http.js +1 -0
- package/build/index.js +7 -7
- package/build/limiter.d.ts +2 -3
- package/build/limiter.js +11 -11
- package/build/logger.d.ts +16 -9
- package/build/logger.js +32 -16
- package/build/monitoring.d.ts +2 -3
- package/build/monitoring.js +4 -4
- package/build/router.d.ts +1 -3
- package/build/router.js +21 -22
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/pdf.post.body.json +8 -8
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chromium/http/content.post.body.json +8 -8
- package/build/routes/chromium/http/pdf.post.body.json +8 -8
- package/build/routes/chromium/http/scrape.post.body.json +8 -8
- package/build/routes/chromium/http/screenshot.post.body.json +8 -8
- package/build/routes/management/http/pressure.get.d.ts +63 -0
- package/build/routes/management/http/pressure.get.js +56 -0
- package/build/routes/management/http/pressure.get.response.json +76 -0
- package/build/routes/management/http/static.get.js +3 -3
- package/build/routes/management/tests/management.spec.js +16 -0
- package/build/server.d.ts +1 -3
- package/build/server.js +33 -30
- package/build/shared/content.http.d.ts +1 -1
- package/build/shared/content.http.js +4 -1
- package/build/shared/download.http.js +9 -9
- package/build/shared/function.http.js +2 -2
- package/build/shared/json-protocol.http.d.ts +3 -3
- package/build/shared/json-protocol.http.js +2 -2
- package/build/shared/json-version.http.d.ts +3 -3
- package/build/shared/json-version.http.js +2 -2
- package/build/shared/pdf.http.d.ts +1 -1
- package/build/shared/pdf.http.js +6 -4
- package/build/shared/performance.http.js +1 -0
- package/build/shared/scrape.http.d.ts +1 -1
- package/build/shared/scrape.http.js +4 -1
- package/build/shared/screenshot.http.d.ts +1 -1
- package/build/shared/screenshot.http.js +4 -1
- package/build/shared/utils/function/handler.js +7 -7
- package/build/shared/utils/performance/child.js +4 -4
- package/build/shared/utils/performance/main.d.ts +1 -1
- package/build/shared/utils/performance/main.js +5 -7
- package/build/shared/utils/performance/types.d.ts +2 -1
- package/build/types.d.ts +10 -1
- package/build/types.js +10 -1
- package/build/utils.d.ts +1 -1
- package/build/utils.js +6 -2
- package/package.json +18 -15
- package/scripts/install-debugger.js +55 -15
- package/src/browserless.ts +25 -12
- package/src/browsers/chrome.cdp.ts +2 -5
- package/src/browsers/chrome.playwright.ts +2 -5
- package/src/browsers/chromium.cdp.ts +84 -35
- package/src/browsers/chromium.playwright.ts +26 -13
- package/src/browsers/firefox.playwright.ts +28 -13
- package/src/browsers/index.ts +24 -16
- package/src/browsers/webkit.playwright.ts +28 -13
- package/src/config.ts +4 -0
- package/src/file-system.ts +2 -7
- package/src/http.ts +1 -0
- package/src/index.ts +7 -7
- package/src/limiter.ts +13 -11
- package/src/logger.ts +39 -18
- package/src/monitoring.ts +6 -8
- package/src/router.ts +20 -20
- package/src/routes/management/http/pressure.get.ts +135 -0
- package/src/routes/management/http/static.get.ts +3 -5
- package/src/routes/management/tests/management.spec.ts +26 -0
- package/src/server.ts +43 -30
- package/src/shared/content.http.ts +5 -1
- package/src/shared/download.http.ts +9 -9
- package/src/shared/function.http.ts +2 -2
- package/src/shared/json-protocol.http.ts +8 -3
- package/src/shared/json-version.http.ts +8 -4
- package/src/shared/pdf.http.ts +6 -4
- package/src/shared/performance.http.ts +1 -0
- package/src/shared/scrape.http.ts +5 -1
- package/src/shared/screenshot.http.ts +4 -1
- package/src/shared/utils/function/handler.ts +7 -7
- package/src/shared/utils/performance/child.ts +4 -4
- package/src/shared/utils/performance/main.ts +5 -6
- package/src/shared/utils/performance/types.ts +2 -1
- package/src/types.ts +9 -0
- package/src/utils.ts +7 -2
- package/static/docs/swagger.json +138 -10
- package/static/docs/swagger.min.json +137 -9
- package/static/function/client.js +4290 -5542
- package/static/function/index.html +4290 -5542
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
# [Latest](https://github.com/browserless/chrome/compare/v2.
|
|
1
|
+
# [Latest](https://github.com/browserless/chrome/compare/v2.10.0...main)
|
|
2
2
|
- Dependency updates.
|
|
3
3
|
|
|
4
|
+
# [v2.10.0](https://github.com/browserless/chrome/compare/v2.9.0...v2.10.0)
|
|
5
|
+
- Adds back in the `/pressure` API from V1.
|
|
6
|
+
- Dependency updates.
|
|
7
|
+
|
|
8
|
+
# [v2.9.0](https://github.com/browserless/chrome/compare/v2.8.0...v2.9.0)
|
|
9
|
+
- Dependency updates.
|
|
10
|
+
- Debugger is now included and mounted under the `/debugger` path.
|
|
11
|
+
- Browserless now uses the `Logger` utility internally. Better logs for certain classes as well.
|
|
12
|
+
- Fix number of connected clients when using the Page websocket route.
|
|
13
|
+
- Allows "HEAD" requests for most "GET"-based APIs.
|
|
14
|
+
-
|
|
15
|
+
|
|
4
16
|
# [v2.8.0](https://github.com/browserless/chrome/compare/v2.7.1...v2.8.0)
|
|
5
17
|
**April 12, 2024**
|
|
6
18
|
**Potentially Breaking**
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- markdownlint-disable first-line-h1 no-emphasis-as-heading -->
|
|
1
|
+
<!-- markdownlint-disable commands-show-output first-line-h1 no-emphasis-as-heading -->
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
@@ -28,6 +28,8 @@ If you've been struggling to deploy headless browsers without running into issue
|
|
|
28
28
|
- [Puppeteer](#puppeteer)
|
|
29
29
|
- [Playwright](#playwright)
|
|
30
30
|
- [Extending (NodeJS SDK)](#extending-nodejs-sdk)
|
|
31
|
+
- [Debugger](#debugger)
|
|
32
|
+
- [Install debugger](#install-debugger)
|
|
31
33
|
- [Usage with other libraries](#usage-with-other-libraries)
|
|
32
34
|
- [Motivations](#motivations)
|
|
33
35
|
- [Licensing](#licensing)
|
|
@@ -70,7 +72,7 @@ You still execute the script itself which gives you total control over what libr
|
|
|
70
72
|
|
|
71
73
|
### Docker
|
|
72
74
|
|
|
73
|
-
> [!TIP]
|
|
75
|
+
> [!TIP]
|
|
74
76
|
> See more options on our [full documentation site](https://docs.browserless.io/Docker/docker-quickstart).
|
|
75
77
|
|
|
76
78
|
1. `docker run -p 3000:3000 ghcr.io/browserless/chromium`
|
|
@@ -107,7 +109,7 @@ const browser = await puppeteer.connect({
|
|
|
107
109
|
|
|
108
110
|
### Playwright
|
|
109
111
|
|
|
110
|
-
We support running with playwright via their their browser's
|
|
112
|
+
We support running with playwright via their their browser's remote connection protocols interface out of the box. Just make sure that your Docker image, playwright browser type _and_ endpoint match:
|
|
111
113
|
|
|
112
114
|
**Before**
|
|
113
115
|
|
|
@@ -137,6 +139,42 @@ After that, the rest of your code remains the same with no other changes require
|
|
|
137
139
|
|
|
138
140
|
Browserless comes with built-in extension capabilities, and allows for extending nearly any aspect of the system (for Version 2+). For more details on how to write your own routes, build docker images, and more, [see our SDK README.md](/bin/scaffold/README.md) or simply run "npx @browserless.io/browserless create" in a terminal and follow the onscreen prompts.
|
|
139
141
|
|
|
142
|
+
## Debugger
|
|
143
|
+
|
|
144
|
+
You can install a first-party interactive debugger for Browserless, that makes writing scripts faster and interactive. You can take advantage of things like `debugger;` calls and the page's console output to see what's happening on the page while your script is running. All of the Chrome devtools are there at your disposal.
|
|
145
|
+
|
|
146
|
+

|
|
147
|
+
|
|
148
|
+
A small list of features includes:
|
|
149
|
+
|
|
150
|
+
- Running `debugger;` and `console.log` calls
|
|
151
|
+
- Errors in the script are caught and show up in the console tab
|
|
152
|
+
- DOM inspection, watch network requests, and even see how the page is rendering
|
|
153
|
+
- Exporting you debugging script as a Node project
|
|
154
|
+
- Everything included in Chrome DevTools
|
|
155
|
+
|
|
156
|
+
### Install debugger
|
|
157
|
+
|
|
158
|
+
Installing the debugger is as simple as running the `install:debugger` script _after_ the project has been built. This way:
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
$ npm run build
|
|
162
|
+
$ npm run install:debugger #or npm install:dev
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
You will then see the debugger url during the startup process.
|
|
166
|
+
|
|
167
|
+
```log
|
|
168
|
+
---------------------------------------------------------
|
|
169
|
+
| browserless.io
|
|
170
|
+
| To read documentation and more, load in your browser:
|
|
171
|
+
|
|
|
172
|
+
| OpenAPI: http://localhost:3000/docs
|
|
173
|
+
| Full Documentation: https://docs.browserless.io/
|
|
174
|
+
| Debbuger: http://localhost:3000/debugger/?token=6R0W53R135510
|
|
175
|
+
---------------------------------------------------------
|
|
176
|
+
```
|
|
177
|
+
|
|
140
178
|
## Usage with other libraries
|
|
141
179
|
|
|
142
180
|
Most libraries allow you to specify a remote instance of Chrome to interact with. They are either looking for a websocket endpoint, a host and port, or some address. Browserless supports these by default, however if you're having issues please make an issue in this project and we'll try and work with the library authors to get them integrated with browserless. Please note that in V2 we no longer support selenium or webdriver integrations.
|
|
Binary file
|
|
@@ -23,10 +23,11 @@ export default class HelloWorldHTTPRoute extends HTTPRoute {
|
|
|
23
23
|
path = '/hello';
|
|
24
24
|
tags = [APITags.management];
|
|
25
25
|
handler = async (
|
|
26
|
-
|
|
27
|
-
_logger: Logger,
|
|
26
|
+
req: Request,
|
|
28
27
|
res: Response,
|
|
28
|
+
logger: Logger,
|
|
29
29
|
): Promise<void> => {
|
|
30
|
+
logger.verbose(`${req.method} /hello was called!`);
|
|
30
31
|
const response: ResponseSchema = 'Hello World!';
|
|
31
32
|
return writeResponse(res, 200, response, contentTypes.text);
|
|
32
33
|
};
|
package/build/browserless.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/// <reference types="debug" />
|
|
2
1
|
/// <reference types="node" />
|
|
3
2
|
/// <reference types="node" />
|
|
4
|
-
import { Logger as BlessLogger, BrowserManager, Config, FileSystem, HTTPServer, Hooks, Limiter, Metrics, Monitoring, Router, Token, WebHooks } from '@browserless.io/browserless';
|
|
3
|
+
import { Logger as BlessLogger, BrowserHTTPRoute, BrowserManager, BrowserWebsocketRoute, Config, FileSystem, HTTPRoute, HTTPServer, Hooks, Limiter, Metrics, Monitoring, Router, Token, WebHooks, WebSocketRoute } from '@browserless.io/browserless';
|
|
5
4
|
import { EventEmitter } from 'events';
|
|
5
|
+
type routeInstances = HTTPRoute | BrowserHTTPRoute | WebSocketRoute | BrowserWebsocketRoute;
|
|
6
6
|
export declare class Browserless extends EventEmitter {
|
|
7
|
-
protected
|
|
7
|
+
protected logger: BlessLogger;
|
|
8
8
|
protected browserManager: BrowserManager;
|
|
9
9
|
protected config: Config;
|
|
10
10
|
protected fileSystem: FileSystem;
|
|
@@ -38,7 +38,7 @@ export declare class Browserless extends EventEmitter {
|
|
|
38
38
|
});
|
|
39
39
|
protected saveMetrics: () => Promise<void>;
|
|
40
40
|
setMetricsSaveInterval: (interval: number) => void;
|
|
41
|
-
|
|
41
|
+
protected routeIsDisabled(route: routeInstances): boolean;
|
|
42
42
|
setStaticSDKDir(dir: string): void;
|
|
43
43
|
disableRoutes(...routeNames: string[]): void;
|
|
44
44
|
addHTTPRoute(httpRouteFilePath: string): void;
|
|
@@ -47,3 +47,4 @@ export declare class Browserless extends EventEmitter {
|
|
|
47
47
|
stop(): Promise<[void | undefined, void, void, void, void, void, void, void, void, void, void]>;
|
|
48
48
|
start(): Promise<void>;
|
|
49
49
|
}
|
|
50
|
+
export {};
|
package/build/browserless.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
-
import { Logger as BlessLogger, BrowserManager, ChromeCDP, ChromiumCDP, ChromiumPlaywright, Config, FileSystem, FirefoxPlaywright, HTTPServer, Hooks, Limiter, Metrics, Monitoring, Router, Token, WebHooks, WebkitPlaywright, availableBrowsers,
|
|
2
|
+
import { Logger as BlessLogger, BrowserManager, ChromeCDP, ChromiumCDP, ChromiumPlaywright, Config, FileSystem, FirefoxPlaywright, HTTPServer, Hooks, Limiter, Metrics, Monitoring, Router, Token, WebHooks, WebkitPlaywright, availableBrowsers, getRouteFiles, makeExternalURL, printLogo, safeParse, } from '@browserless.io/browserless';
|
|
3
3
|
import { EventEmitter } from 'events';
|
|
4
4
|
import { readFile } from 'fs/promises';
|
|
5
5
|
import { userInfo } from 'os';
|
|
6
6
|
const routeSchemas = ['body', 'query'];
|
|
7
7
|
export class Browserless extends EventEmitter {
|
|
8
|
-
|
|
8
|
+
logger;
|
|
9
9
|
browserManager;
|
|
10
10
|
config;
|
|
11
11
|
fileSystem;
|
|
@@ -27,6 +27,7 @@ export class Browserless extends EventEmitter {
|
|
|
27
27
|
constructor({ browserManager, config, fileSystem, hooks, limiter, Logger: LoggerOverride, metrics, monitoring, router, token, webhooks, } = {}) {
|
|
28
28
|
super();
|
|
29
29
|
this.Logger = LoggerOverride ?? BlessLogger;
|
|
30
|
+
this.logger = new this.Logger('index');
|
|
30
31
|
this.config = config || new Config();
|
|
31
32
|
this.metrics = metrics || new Metrics();
|
|
32
33
|
this.token = token || new Token(this.config);
|
|
@@ -53,7 +54,7 @@ export class Browserless extends EventEmitter {
|
|
|
53
54
|
memory,
|
|
54
55
|
};
|
|
55
56
|
this.metrics.reset();
|
|
56
|
-
this.
|
|
57
|
+
this.logger.info(`Current period usage: ${JSON.stringify({
|
|
57
58
|
date: aggregatedStats.date,
|
|
58
59
|
error: aggregatedStats.error,
|
|
59
60
|
maxConcurrent: aggregatedStats.maxConcurrent,
|
|
@@ -67,7 +68,7 @@ export class Browserless extends EventEmitter {
|
|
|
67
68
|
units: aggregatedStats.units,
|
|
68
69
|
})}`);
|
|
69
70
|
if (metricsPath) {
|
|
70
|
-
this.
|
|
71
|
+
this.logger.info(`Saving metrics to "${metricsPath}"`);
|
|
71
72
|
this.fileSystem.append(metricsPath, JSON.stringify(aggregatedStats), false);
|
|
72
73
|
}
|
|
73
74
|
};
|
|
@@ -127,10 +128,13 @@ export class Browserless extends EventEmitter {
|
|
|
127
128
|
WebkitPlaywright,
|
|
128
129
|
];
|
|
129
130
|
const [[internalHttpRouteFiles, internalWsRouteFiles], installedBrowsers] = await Promise.all([getRouteFiles(this.config), availableBrowsers]);
|
|
131
|
+
const hasDebugger = await this.config.hasDebugger();
|
|
132
|
+
const debuggerURL = hasDebugger &&
|
|
133
|
+
makeExternalURL(this.config.getExternalAddress(), `/debugger/?token=${this.config.getToken()}`);
|
|
130
134
|
const docsLink = makeExternalURL(this.config.getExternalAddress(), '/docs');
|
|
131
|
-
this.
|
|
132
|
-
this.
|
|
133
|
-
this.
|
|
135
|
+
this.logger.info(printLogo(docsLink, debuggerURL));
|
|
136
|
+
this.logger.info(`Running as user "${userInfo().username}"`);
|
|
137
|
+
this.logger.info('Starting import of HTTP Routes');
|
|
134
138
|
for (const httpRoute of [
|
|
135
139
|
...this.httpRouteFiles,
|
|
136
140
|
...internalHttpRouteFiles,
|
|
@@ -143,11 +147,12 @@ export class Browserless extends EventEmitter {
|
|
|
143
147
|
}));
|
|
144
148
|
const routeImport = `${this.config.getIsWin() ? 'file:///' : ''}${httpRoute}`;
|
|
145
149
|
const { default: Route, } = await import(routeImport + `?cb=${Date.now()}`);
|
|
146
|
-
const route = new Route(this.browserManager, this.config, this.fileSystem, this.metrics, this.monitoring, this.staticSDKDir);
|
|
150
|
+
const route = new Route(this.browserManager, this.config, this.fileSystem, this.metrics, this.monitoring, this.staticSDKDir, this.limiter);
|
|
147
151
|
if (!this.routeIsDisabled(route)) {
|
|
148
152
|
route.bodySchema = safeParse(bodySchema);
|
|
149
153
|
route.querySchema = safeParse(querySchema);
|
|
150
154
|
route.config = () => this.config;
|
|
155
|
+
route.limiter = () => this.limiter;
|
|
151
156
|
route.metrics = () => this.metrics;
|
|
152
157
|
route.monitoring = () => this.monitoring;
|
|
153
158
|
route.fileSystem = () => this.fileSystem;
|
|
@@ -156,7 +161,7 @@ export class Browserless extends EventEmitter {
|
|
|
156
161
|
}
|
|
157
162
|
}
|
|
158
163
|
}
|
|
159
|
-
this.
|
|
164
|
+
this.logger.info('Starting import of WebSocket Routes');
|
|
160
165
|
for (const wsRoute of [
|
|
161
166
|
...this.webSocketRouteFiles,
|
|
162
167
|
...internalWsRouteFiles,
|
|
@@ -169,10 +174,11 @@ export class Browserless extends EventEmitter {
|
|
|
169
174
|
}));
|
|
170
175
|
const wsImport = `${this.config.getIsWin() ? 'file:///' : ''}${wsRoute}`;
|
|
171
176
|
const { default: Route, } = await import(wsImport + `?cb=${Date.now()}`);
|
|
172
|
-
const route = new Route(this.browserManager, this.config, this.fileSystem, this.metrics, this.monitoring, this.staticSDKDir);
|
|
177
|
+
const route = new Route(this.browserManager, this.config, this.fileSystem, this.metrics, this.monitoring, this.staticSDKDir, this.limiter);
|
|
173
178
|
if (!this.routeIsDisabled(route)) {
|
|
174
179
|
route.querySchema = safeParse(querySchema);
|
|
175
180
|
route.config = () => this.config;
|
|
181
|
+
route.limiter = () => this.limiter;
|
|
176
182
|
route.metrics = () => this.metrics;
|
|
177
183
|
route.monitoring = () => this.monitoring;
|
|
178
184
|
route.fileSystem = () => this.fileSystem;
|
|
@@ -195,14 +201,14 @@ export class Browserless extends EventEmitter {
|
|
|
195
201
|
.filter((e, i, a) => a.findIndex((r) => r.name === e.name) !== i)
|
|
196
202
|
.map((r) => r.name);
|
|
197
203
|
if (duplicateNamedRoutes.length) {
|
|
198
|
-
this.
|
|
204
|
+
this.logger.warn(`Found duplicate routing names. Route names must be unique:`, duplicateNamedRoutes);
|
|
199
205
|
}
|
|
200
206
|
httpRoutes.forEach((r) => this.router.registerHTTPRoute(r));
|
|
201
207
|
wsRoutes.forEach((r) => this.router.registerWebSocketRoute(r));
|
|
202
|
-
this.
|
|
208
|
+
this.logger.info(`Imported and validated all route files, starting up server.`);
|
|
203
209
|
this.server = new HTTPServer(this.config, this.metrics, this.token, this.router, this.hooks, this.Logger);
|
|
204
210
|
await this.server.start();
|
|
205
|
-
this.
|
|
211
|
+
this.logger.info(`Starting metrics collection.`);
|
|
206
212
|
this.metricsSaveIntervalID = setInterval(() => this.saveMetrics(), this.metricsSaveInterval);
|
|
207
213
|
}
|
|
208
214
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { Logger } from '@browserless.io/browserless';
|
|
2
2
|
import { ChromiumCDP } from './chromium.cdp.js';
|
|
3
3
|
export declare class ChromeCDP extends ChromiumCDP {
|
|
4
4
|
protected executablePath: string;
|
|
5
|
-
protected
|
|
5
|
+
protected logger: Logger;
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Logger, chromeExecutablePath } from '@browserless.io/browserless';
|
|
2
2
|
import { ChromiumCDP } from './chromium.cdp.js';
|
|
3
3
|
export class ChromeCDP extends ChromiumCDP {
|
|
4
4
|
executablePath = chromeExecutablePath();
|
|
5
|
-
|
|
5
|
+
logger = new Logger('browsers:chrome:cdp');
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { Logger } from '@browserless.io/browserless';
|
|
2
2
|
import { ChromiumPlaywright } from './chromium.playwright.js';
|
|
3
3
|
export declare class ChromePlaywright extends ChromiumPlaywright {
|
|
4
4
|
protected executablePath: string;
|
|
5
|
-
protected
|
|
5
|
+
protected logger: Logger;
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Logger, chromeExecutablePath } from '@browserless.io/browserless';
|
|
2
2
|
import { ChromiumPlaywright } from './chromium.playwright.js';
|
|
3
3
|
export class ChromePlaywright extends ChromiumPlaywright {
|
|
4
4
|
executablePath = chromeExecutablePath();
|
|
5
|
-
|
|
5
|
+
logger = new Logger('browsers:chrome:playwright');
|
|
6
6
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
/// <reference types="debug" />
|
|
2
1
|
/// <reference types="node" />
|
|
3
2
|
/// <reference types="node" />
|
|
4
3
|
/// <reference types="node" />
|
|
5
4
|
/// <reference types="node" />
|
|
6
5
|
/// <reference types="node" />
|
|
7
|
-
import { CDPLaunchOptions, Config, Request } from '@browserless.io/browserless';
|
|
6
|
+
import { CDPLaunchOptions, Config, Logger, Request } from '@browserless.io/browserless';
|
|
8
7
|
import { Browser, Page, Target } from 'puppeteer-core';
|
|
9
8
|
import { Duplex } from 'stream';
|
|
10
9
|
import { EventEmitter } from 'events';
|
|
@@ -17,12 +16,13 @@ export declare class ChromiumCDP extends EventEmitter {
|
|
|
17
16
|
protected browser: Browser | null;
|
|
18
17
|
protected browserWSEndpoint: string | null;
|
|
19
18
|
protected port?: number;
|
|
20
|
-
protected
|
|
19
|
+
protected logger: Logger;
|
|
21
20
|
protected proxy: httpProxy<import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>>;
|
|
22
21
|
protected executablePath: string;
|
|
23
|
-
constructor({ blockAds, config, userDataDir, }: {
|
|
22
|
+
constructor({ blockAds, config, userDataDir, logger, }: {
|
|
24
23
|
blockAds: boolean;
|
|
25
24
|
config: Config;
|
|
25
|
+
logger: Logger;
|
|
26
26
|
userDataDir: ChromiumCDP['userDataDir'];
|
|
27
27
|
});
|
|
28
28
|
protected cleanListeners(): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BLESS_PAGE_IDENTIFIER, ServerError,
|
|
1
|
+
import { BLESS_PAGE_IDENTIFIER, ServerError, noop, once, } from '@browserless.io/browserless';
|
|
2
2
|
import puppeteer from 'puppeteer-core';
|
|
3
3
|
import { EventEmitter } from 'events';
|
|
4
4
|
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
|
@@ -17,15 +17,16 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
17
17
|
browser = null;
|
|
18
18
|
browserWSEndpoint = null;
|
|
19
19
|
port;
|
|
20
|
-
|
|
20
|
+
logger;
|
|
21
21
|
proxy = httpProxy.createProxyServer();
|
|
22
22
|
executablePath = playwright.chromium.executablePath();
|
|
23
|
-
constructor({ blockAds, config, userDataDir, }) {
|
|
23
|
+
constructor({ blockAds, config, userDataDir, logger, }) {
|
|
24
24
|
super();
|
|
25
25
|
this.userDataDir = userDataDir;
|
|
26
26
|
this.config = config;
|
|
27
27
|
this.blockAds = blockAds;
|
|
28
|
-
this.
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.logger.info(`Starting new ${this.constructor.name} instance`);
|
|
29
30
|
}
|
|
30
31
|
cleanListeners() {
|
|
31
32
|
this.browser?.removeAllListeners();
|
|
@@ -38,27 +39,44 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
38
39
|
onTargetCreated = async (target) => {
|
|
39
40
|
if (target.type() === 'page') {
|
|
40
41
|
const page = await target.page().catch((e) => {
|
|
41
|
-
this.
|
|
42
|
+
this.logger.error(`Error in ${this.constructor.name} new page ${e}`);
|
|
42
43
|
return null;
|
|
43
44
|
});
|
|
44
45
|
if (page) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
46
|
+
this.logger.trace(`Setting up file:// protocol request rejection`);
|
|
47
|
+
page.on('error', (err) => {
|
|
48
|
+
this.logger.error(err);
|
|
49
|
+
});
|
|
50
|
+
page.on('pageerror', (err) => {
|
|
51
|
+
this.logger.warn(err);
|
|
52
|
+
});
|
|
53
|
+
page.on('framenavigated', (frame) => {
|
|
54
|
+
this.logger.trace(`Navigation to ${frame.url()}`);
|
|
55
|
+
});
|
|
56
|
+
page.on('console', (message) => {
|
|
57
|
+
this.logger.trace(`${message.type()}: ${message.text()}`);
|
|
58
|
+
});
|
|
59
|
+
page.on('requestfailed', (req) => {
|
|
60
|
+
this.logger.warn(`"${req.failure()?.errorText}": ${req.url()}`);
|
|
61
|
+
});
|
|
62
|
+
page.on('request', async (request) => {
|
|
63
|
+
this.logger.trace(`${request.method()}: ${request.url()}`);
|
|
64
|
+
if (!this.config.getAllowFileProtocol() &&
|
|
65
|
+
request.url().startsWith('file://')) {
|
|
66
|
+
this.logger.error(`File protocol request found in request to ${this.constructor.name}, terminating`);
|
|
67
|
+
page.close().catch(noop);
|
|
68
|
+
this.close();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
page.on('response', async (response) => {
|
|
72
|
+
this.logger.trace(`${response.status()}: ${response.url()}`);
|
|
73
|
+
if (!this.config.getAllowFileProtocol() &&
|
|
74
|
+
response.url().startsWith('file://')) {
|
|
75
|
+
this.logger.error(`File protocol request found in response to ${this.constructor.name}, terminating`);
|
|
76
|
+
page.close().catch(noop);
|
|
77
|
+
this.close();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
62
80
|
this.emit('newPage', page);
|
|
63
81
|
}
|
|
64
82
|
}
|
|
@@ -66,13 +84,13 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
66
84
|
isRunning = () => this.running;
|
|
67
85
|
newPage = async () => {
|
|
68
86
|
if (!this.browser) {
|
|
69
|
-
throw new ServerError(
|
|
87
|
+
throw new ServerError(`${this.constructor.name} hasn't been launched yet!`);
|
|
70
88
|
}
|
|
71
89
|
return this.browser.newPage();
|
|
72
90
|
};
|
|
73
91
|
close = async () => {
|
|
74
92
|
if (this.browser) {
|
|
75
|
-
this.
|
|
93
|
+
this.logger.info(`Closing ${this.constructor.name} process and all listeners`);
|
|
76
94
|
this.emit('close');
|
|
77
95
|
this.cleanListeners();
|
|
78
96
|
this.browser.removeAllListeners();
|
|
@@ -86,7 +104,7 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
86
104
|
process = () => this.browser?.process() || null;
|
|
87
105
|
launch = async (options = {}) => {
|
|
88
106
|
this.port = await getPort();
|
|
89
|
-
this.
|
|
107
|
+
this.logger.info(`${this.constructor.name} got open port ${this.port}`);
|
|
90
108
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
91
109
|
const finalOptions = {
|
|
92
110
|
...options,
|
|
@@ -107,13 +125,12 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
107
125
|
const launch = options.stealth
|
|
108
126
|
? puppeteerStealth.launch.bind(puppeteerStealth)
|
|
109
127
|
: puppeteer.launch.bind(puppeteer);
|
|
110
|
-
this.
|
|
111
|
-
// @ts-ignore mis-matched types from stealth...
|
|
128
|
+
this.logger.info(finalOptions, `Launching ${this.constructor.name} Handler`);
|
|
112
129
|
this.browser = (await launch(finalOptions));
|
|
113
130
|
this.browser.on('targetcreated', this.onTargetCreated);
|
|
114
131
|
this.running = true;
|
|
115
132
|
this.browserWSEndpoint = this.browser.wsEndpoint();
|
|
116
|
-
this.
|
|
133
|
+
this.logger.info(`${this.constructor.name} is running on ${this.browserWSEndpoint}`);
|
|
117
134
|
return this.browser;
|
|
118
135
|
};
|
|
119
136
|
wsEndpoint = () => this.browserWSEndpoint;
|
|
@@ -134,7 +151,7 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
134
151
|
throw new ServerError(`No browserWSEndpoint found, did you launch first?`);
|
|
135
152
|
}
|
|
136
153
|
socket.once('close', resolve);
|
|
137
|
-
this.
|
|
154
|
+
this.logger.info(`Proxying ${req.parsed.href} to ${this.constructor.name}`);
|
|
138
155
|
const shouldMakePage = req.parsed.pathname.includes(BLESS_PAGE_IDENTIFIER);
|
|
139
156
|
const page = shouldMakePage ? await this.browser.newPage() : null;
|
|
140
157
|
const pathname = page
|
|
@@ -148,7 +165,7 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
148
165
|
changeOrigin: true,
|
|
149
166
|
target,
|
|
150
167
|
}, (error) => {
|
|
151
|
-
this.
|
|
168
|
+
this.logger.error(`Error proxying session to ${this.constructor.name}: ${error}`);
|
|
152
169
|
this.close();
|
|
153
170
|
return reject(error);
|
|
154
171
|
});
|
|
@@ -166,7 +183,7 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
166
183
|
this.browser?.once('close', close);
|
|
167
184
|
this.browser?.process()?.once('close', close);
|
|
168
185
|
socket.once('close', close);
|
|
169
|
-
this.
|
|
186
|
+
this.logger.info(`Proxying ${req.parsed.href} to ${this.constructor.name} ${this.browserWSEndpoint}`);
|
|
170
187
|
req.url = '';
|
|
171
188
|
// Delete headers known to cause issues
|
|
172
189
|
delete req.headers.origin;
|
|
@@ -174,7 +191,7 @@ export class ChromiumCDP extends EventEmitter {
|
|
|
174
191
|
changeOrigin: true,
|
|
175
192
|
target: this.browserWSEndpoint,
|
|
176
193
|
}, (error) => {
|
|
177
|
-
this.
|
|
194
|
+
this.logger.error(`Error proxying session to ${this.constructor.name}: ${error}`);
|
|
178
195
|
this.close();
|
|
179
196
|
return reject(error);
|
|
180
197
|
});
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
/// <reference types="debug" />
|
|
3
2
|
/// <reference types="node" />
|
|
4
3
|
/// <reference types="node" />
|
|
5
4
|
/// <reference types="node" />
|
|
6
|
-
import { BrowserServerOptions, Config, Request } from '@browserless.io/browserless';
|
|
5
|
+
import { BrowserServerOptions, Config, Logger, Request } from '@browserless.io/browserless';
|
|
7
6
|
import playwright, { Page } from 'playwright-core';
|
|
8
7
|
import { Duplex } from 'stream';
|
|
9
8
|
import { EventEmitter } from 'events';
|
|
@@ -12,13 +11,14 @@ export declare class ChromiumPlaywright extends EventEmitter {
|
|
|
12
11
|
protected config: Config;
|
|
13
12
|
protected userDataDir: string | null;
|
|
14
13
|
protected running: boolean;
|
|
14
|
+
protected logger: Logger;
|
|
15
15
|
protected proxy: httpProxy<import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>>;
|
|
16
16
|
protected browser: playwright.BrowserServer | null;
|
|
17
17
|
protected browserWSEndpoint: string | null;
|
|
18
|
-
protected debug: import("debug").Debugger;
|
|
19
18
|
protected executablePath: string;
|
|
20
|
-
constructor({ config, userDataDir, }: {
|
|
19
|
+
constructor({ config, userDataDir, logger, }: {
|
|
21
20
|
config: Config;
|
|
21
|
+
logger: Logger;
|
|
22
22
|
userDataDir: ChromiumPlaywright['userDataDir'];
|
|
23
23
|
});
|
|
24
24
|
protected cleanListeners(): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ServerError,
|
|
1
|
+
import { ServerError, } from '@browserless.io/browserless';
|
|
2
2
|
import playwright from 'playwright-core';
|
|
3
3
|
import { EventEmitter } from 'events';
|
|
4
4
|
import httpProxy from 'http-proxy';
|
|
@@ -7,16 +7,17 @@ export class ChromiumPlaywright extends EventEmitter {
|
|
|
7
7
|
config;
|
|
8
8
|
userDataDir;
|
|
9
9
|
running = false;
|
|
10
|
+
logger;
|
|
10
11
|
proxy = httpProxy.createProxyServer();
|
|
11
12
|
browser = null;
|
|
12
13
|
browserWSEndpoint = null;
|
|
13
|
-
debug = createLogger('browsers:chromium:playwright');
|
|
14
14
|
executablePath = playwright.chromium.executablePath();
|
|
15
|
-
constructor({ config, userDataDir, }) {
|
|
15
|
+
constructor({ config, userDataDir, logger, }) {
|
|
16
16
|
super();
|
|
17
17
|
this.userDataDir = userDataDir;
|
|
18
18
|
this.config = config;
|
|
19
|
-
this.
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
this.logger.info(`Starting new ${this.constructor.name} instance`);
|
|
20
21
|
}
|
|
21
22
|
cleanListeners() {
|
|
22
23
|
this.removeAllListeners();
|
|
@@ -24,7 +25,7 @@ export class ChromiumPlaywright extends EventEmitter {
|
|
|
24
25
|
isRunning = () => this.running;
|
|
25
26
|
close = async () => {
|
|
26
27
|
if (this.browser) {
|
|
27
|
-
this.
|
|
28
|
+
this.logger.info(`Closing ${this.constructor.name} process and all listeners`);
|
|
28
29
|
this.emit('close');
|
|
29
30
|
this.cleanListeners();
|
|
30
31
|
this.browser.close();
|
|
@@ -35,20 +36,20 @@ export class ChromiumPlaywright extends EventEmitter {
|
|
|
35
36
|
};
|
|
36
37
|
pages = async () => [];
|
|
37
38
|
getPageId = () => {
|
|
38
|
-
throw new ServerError(`#getPageId is not yet supported with this
|
|
39
|
+
throw new ServerError(`#getPageId is not yet supported with ${this.constructor.name}.`);
|
|
39
40
|
};
|
|
40
41
|
makeLiveURL = () => {
|
|
41
|
-
throw new ServerError(`Live URLs are not yet supported with this
|
|
42
|
+
throw new ServerError(`Live URLs are not yet supported with ${this.constructor.name}. In the future this will be at "${this.config.getExternalAddress()}"`);
|
|
42
43
|
};
|
|
43
44
|
newPage = async () => {
|
|
44
45
|
if (!this.browser || !this.browserWSEndpoint) {
|
|
45
|
-
throw new ServerError(
|
|
46
|
+
throw new ServerError(`${this.constructor.name} hasn't been launched yet!`);
|
|
46
47
|
}
|
|
47
48
|
const browser = await playwright.chromium.connect(this.browserWSEndpoint);
|
|
48
49
|
return await browser.newPage();
|
|
49
50
|
};
|
|
50
51
|
launch = async (options = {}) => {
|
|
51
|
-
this.
|
|
52
|
+
this.logger.info(`Launching ${this.constructor.name} Handler`);
|
|
52
53
|
this.browser = await playwright.chromium.launchServer({
|
|
53
54
|
...options,
|
|
54
55
|
args: [
|
|
@@ -59,7 +60,7 @@ export class ChromiumPlaywright extends EventEmitter {
|
|
|
59
60
|
executablePath: this.executablePath,
|
|
60
61
|
});
|
|
61
62
|
const browserWSEndpoint = this.browser.wsEndpoint();
|
|
62
|
-
this.
|
|
63
|
+
this.logger.info(`${this.constructor.name} is running on ${browserWSEndpoint}`);
|
|
63
64
|
this.running = true;
|
|
64
65
|
this.browserWSEndpoint = browserWSEndpoint;
|
|
65
66
|
return this.browser;
|
|
@@ -78,14 +79,14 @@ export class ChromiumPlaywright extends EventEmitter {
|
|
|
78
79
|
return externalURL.href;
|
|
79
80
|
};
|
|
80
81
|
proxyPageWebSocket = async () => {
|
|
81
|
-
|
|
82
|
+
this.logger.warn(`${this.constructor.name} Not yet implemented`);
|
|
82
83
|
};
|
|
83
84
|
proxyWebSocket = async (req, socket, head) => new Promise((resolve, reject) => {
|
|
84
85
|
if (!this.browserWSEndpoint) {
|
|
85
86
|
throw new ServerError(`No browserWSEndpoint found, did you launch first?`);
|
|
86
87
|
}
|
|
87
88
|
socket.once('close', resolve);
|
|
88
|
-
this.
|
|
89
|
+
this.logger.info(`Proxying ${req.parsed.href} to ${this.constructor.name} ${this.browserWSEndpoint}`);
|
|
89
90
|
// Delete headers known to cause issues
|
|
90
91
|
delete req.headers.origin;
|
|
91
92
|
req.url = '';
|
|
@@ -93,7 +94,7 @@ export class ChromiumPlaywright extends EventEmitter {
|
|
|
93
94
|
changeOrigin: true,
|
|
94
95
|
target: this.browserWSEndpoint,
|
|
95
96
|
}, (error) => {
|
|
96
|
-
this.
|
|
97
|
+
this.logger.error(`Error proxying session to ${this.constructor.name}: ${error}`);
|
|
97
98
|
this.close();
|
|
98
99
|
return reject(error);
|
|
99
100
|
});
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
/// <reference types="debug" />
|
|
3
2
|
/// <reference types="node" />
|
|
4
3
|
/// <reference types="node" />
|
|
5
4
|
/// <reference types="node" />
|
|
6
|
-
import { BrowserServerOptions, Config, Request } from '@browserless.io/browserless';
|
|
5
|
+
import { BrowserServerOptions, Config, Logger, Request } from '@browserless.io/browserless';
|
|
7
6
|
import playwright, { Page } from 'playwright-core';
|
|
8
7
|
import { Duplex } from 'stream';
|
|
9
8
|
import { EventEmitter } from 'events';
|
|
@@ -12,12 +11,13 @@ export declare class FirefoxPlaywright extends EventEmitter {
|
|
|
12
11
|
protected config: Config;
|
|
13
12
|
protected userDataDir: string | null;
|
|
14
13
|
protected running: boolean;
|
|
14
|
+
protected logger: Logger;
|
|
15
15
|
protected proxy: httpProxy<import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>>;
|
|
16
16
|
protected browser: playwright.BrowserServer | null;
|
|
17
17
|
protected browserWSEndpoint: string | null;
|
|
18
|
-
|
|
19
|
-
constructor({ config, userDataDir, }: {
|
|
18
|
+
constructor({ config, userDataDir, logger, }: {
|
|
20
19
|
config: Config;
|
|
20
|
+
logger: Logger;
|
|
21
21
|
userDataDir: FirefoxPlaywright['userDataDir'];
|
|
22
22
|
});
|
|
23
23
|
protected cleanListeners(): void;
|