@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.
Files changed (115) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/README.md +41 -3
  3. package/assets/debugger.png +0 -0
  4. package/bin/scaffold/src/hello-world.http.ts +3 -2
  5. package/build/browserless.d.ts +5 -4
  6. package/build/browserless.js +19 -13
  7. package/build/browsers/chrome.cdp.d.ts +2 -2
  8. package/build/browsers/chrome.cdp.js +2 -2
  9. package/build/browsers/chrome.playwright.d.ts +2 -2
  10. package/build/browsers/chrome.playwright.js +2 -2
  11. package/build/browsers/chromium.cdp.d.ts +4 -4
  12. package/build/browsers/chromium.cdp.js +49 -32
  13. package/build/browsers/chromium.playwright.d.ts +4 -4
  14. package/build/browsers/chromium.playwright.js +14 -13
  15. package/build/browsers/firefox.playwright.d.ts +4 -4
  16. package/build/browsers/firefox.playwright.js +14 -13
  17. package/build/browsers/index.d.ts +24 -8
  18. package/build/browsers/index.js +20 -15
  19. package/build/browsers/webkit.playwright.d.ts +4 -4
  20. package/build/browsers/webkit.playwright.js +14 -13
  21. package/build/config.d.ts +3 -0
  22. package/build/config.js +3 -0
  23. package/build/file-system.d.ts +2 -3
  24. package/build/file-system.js +2 -2
  25. package/build/http.d.ts +1 -0
  26. package/build/http.js +1 -0
  27. package/build/index.js +7 -7
  28. package/build/limiter.d.ts +2 -3
  29. package/build/limiter.js +11 -11
  30. package/build/logger.d.ts +16 -9
  31. package/build/logger.js +32 -16
  32. package/build/monitoring.d.ts +2 -3
  33. package/build/monitoring.js +4 -4
  34. package/build/router.d.ts +1 -3
  35. package/build/router.js +21 -22
  36. package/build/routes/chrome/http/content.post.body.json +8 -8
  37. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  38. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  39. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  40. package/build/routes/chromium/http/content.post.body.json +8 -8
  41. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  42. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  43. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  44. package/build/routes/management/http/pressure.get.d.ts +63 -0
  45. package/build/routes/management/http/pressure.get.js +56 -0
  46. package/build/routes/management/http/pressure.get.response.json +76 -0
  47. package/build/routes/management/http/static.get.js +3 -3
  48. package/build/routes/management/tests/management.spec.js +16 -0
  49. package/build/server.d.ts +1 -3
  50. package/build/server.js +33 -30
  51. package/build/shared/content.http.d.ts +1 -1
  52. package/build/shared/content.http.js +4 -1
  53. package/build/shared/download.http.js +9 -9
  54. package/build/shared/function.http.js +2 -2
  55. package/build/shared/json-protocol.http.d.ts +3 -3
  56. package/build/shared/json-protocol.http.js +2 -2
  57. package/build/shared/json-version.http.d.ts +3 -3
  58. package/build/shared/json-version.http.js +2 -2
  59. package/build/shared/pdf.http.d.ts +1 -1
  60. package/build/shared/pdf.http.js +6 -4
  61. package/build/shared/performance.http.js +1 -0
  62. package/build/shared/scrape.http.d.ts +1 -1
  63. package/build/shared/scrape.http.js +4 -1
  64. package/build/shared/screenshot.http.d.ts +1 -1
  65. package/build/shared/screenshot.http.js +4 -1
  66. package/build/shared/utils/function/handler.js +7 -7
  67. package/build/shared/utils/performance/child.js +4 -4
  68. package/build/shared/utils/performance/main.d.ts +1 -1
  69. package/build/shared/utils/performance/main.js +5 -7
  70. package/build/shared/utils/performance/types.d.ts +2 -1
  71. package/build/types.d.ts +10 -1
  72. package/build/types.js +10 -1
  73. package/build/utils.d.ts +1 -1
  74. package/build/utils.js +6 -2
  75. package/package.json +18 -15
  76. package/scripts/install-debugger.js +55 -15
  77. package/src/browserless.ts +25 -12
  78. package/src/browsers/chrome.cdp.ts +2 -5
  79. package/src/browsers/chrome.playwright.ts +2 -5
  80. package/src/browsers/chromium.cdp.ts +84 -35
  81. package/src/browsers/chromium.playwright.ts +26 -13
  82. package/src/browsers/firefox.playwright.ts +28 -13
  83. package/src/browsers/index.ts +24 -16
  84. package/src/browsers/webkit.playwright.ts +28 -13
  85. package/src/config.ts +4 -0
  86. package/src/file-system.ts +2 -7
  87. package/src/http.ts +1 -0
  88. package/src/index.ts +7 -7
  89. package/src/limiter.ts +13 -11
  90. package/src/logger.ts +39 -18
  91. package/src/monitoring.ts +6 -8
  92. package/src/router.ts +20 -20
  93. package/src/routes/management/http/pressure.get.ts +135 -0
  94. package/src/routes/management/http/static.get.ts +3 -5
  95. package/src/routes/management/tests/management.spec.ts +26 -0
  96. package/src/server.ts +43 -30
  97. package/src/shared/content.http.ts +5 -1
  98. package/src/shared/download.http.ts +9 -9
  99. package/src/shared/function.http.ts +2 -2
  100. package/src/shared/json-protocol.http.ts +8 -3
  101. package/src/shared/json-version.http.ts +8 -4
  102. package/src/shared/pdf.http.ts +6 -4
  103. package/src/shared/performance.http.ts +1 -0
  104. package/src/shared/scrape.http.ts +5 -1
  105. package/src/shared/screenshot.http.ts +4 -1
  106. package/src/shared/utils/function/handler.ts +7 -7
  107. package/src/shared/utils/performance/child.ts +4 -4
  108. package/src/shared/utils/performance/main.ts +5 -6
  109. package/src/shared/utils/performance/types.ts +2 -1
  110. package/src/types.ts +9 -0
  111. package/src/utils.ts +7 -2
  112. package/static/docs/swagger.json +138 -10
  113. package/static/docs/swagger.min.json +137 -9
  114. package/static/function/client.js +4290 -5542
  115. package/static/function/index.html +4290 -5542
package/CHANGELOG.md CHANGED
@@ -1,6 +1,18 @@
1
- # [Latest](https://github.com/browserless/chrome/compare/v2.8.0...main)
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
  ![browserless.io logo](/assets/logo.png)
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 remot connection protocols interface out of the box. Just make sure that your Docker image, playwright browser type _and_ endpoint match:
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
+ ![browserless.io logo](/assets/debugger.png)
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
- _req: Request,
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
  };
@@ -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 debug: debug.Debugger;
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
- private routeIsDisabled;
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 {};
@@ -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, createLogger, getRouteFiles, makeExternalURL, printLogo, safeParse, } from '@browserless.io/browserless';
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
- debug = createLogger('index');
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.debug(`Current period usage: ${JSON.stringify({
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.debug(`Saving metrics to "${metricsPath}"`);
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.debug(printLogo(docsLink));
132
- this.debug(`Running as user "${userInfo().username}"`);
133
- this.debug('Starting import of HTTP Routes');
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.debug('Starting import of WebSocket Routes');
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.debug(`Found duplicate routing names. Route names must be unique:`, duplicateNamedRoutes);
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.debug(`Imported and validated all route files, starting up server.`);
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.debug(`Starting metrics collection.`);
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
- /// <reference types="debug" />
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 debug: import("debug").Debugger;
5
+ protected logger: Logger;
6
6
  }
@@ -1,6 +1,6 @@
1
- import { chromeExecutablePath, createLogger, } from '@browserless.io/browserless';
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
- debug = createLogger('browsers:chrome:cdp');
5
+ logger = new Logger('browsers:chrome:cdp');
6
6
  }
@@ -1,6 +1,6 @@
1
- /// <reference types="debug" />
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 debug: import("debug").Debugger;
5
+ protected logger: Logger;
6
6
  }
@@ -1,6 +1,6 @@
1
- import { chromeExecutablePath, createLogger, } from '@browserless.io/browserless';
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
- debug = createLogger('browsers:chrome:playwright');
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 debug: import("debug").Debugger;
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, createLogger, noop, once, } from '@browserless.io/browserless';
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
- debug = createLogger('browsers:chromium:cdp');
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.debug(`Starting new browser instance`);
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.debug(`Error in new page ${e}`);
42
+ this.logger.error(`Error in ${this.constructor.name} new page ${e}`);
42
43
  return null;
43
44
  });
44
45
  if (page) {
45
- if (!this.config.getAllowFileProtocol()) {
46
- this.debug(`Setting up file:// protocol request rejection`);
47
- page.on('request', async (request) => {
48
- if (request.url().startsWith('file://')) {
49
- this.debug(`File protocol request found in request, terminating`);
50
- page.close().catch(noop);
51
- this.close();
52
- }
53
- });
54
- page.on('response', async (response) => {
55
- if (response.url().startsWith('file://')) {
56
- this.debug(`File protocol request found in response, terminating`);
57
- page.close().catch(noop);
58
- this.close();
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(`Browser hasn't been launched yet!`);
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.debug(`Closing browser process and all listeners`);
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.debug(`Got open port ${this.port}`);
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.debug(finalOptions, `Launching CDP Handler`);
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.debug(`Browser is running on ${this.browserWSEndpoint}`);
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.debug(`Proxying ${req.parsed.href}`);
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.debug(`Error proxying session: ${error}`);
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.debug(`Proxying ${req.parsed.href} to browser ${this.browserWSEndpoint}`);
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.debug(`Error proxying session: ${error}`);
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, createLogger, } from '@browserless.io/browserless';
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.debug(`Starting new browser instance`);
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.debug(`Closing browser process and all listeners`);
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 browser.`);
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 browser. In the future this will be at "${this.config.getExternalAddress()}"`);
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(`Browser hasn't been launched yet!`);
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.debug(`Launching Playwright Handler`);
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.debug(`Browser is running on ${browserWSEndpoint}`);
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
- console.warn(`Not yet implemented`);
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.debug(`Proxying ${req.parsed.href} to browser ${this.browserWSEndpoint}`);
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.debug(`Error proxying session: ${error}`);
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
- protected debug: import("debug").Debugger;
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;