@browserless.io/browserless 2.7.1 → 2.9.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 (138) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/README.md +41 -3
  3. package/assets/debugger.png +0 -0
  4. package/bin/browserless.js +8 -4
  5. package/bin/scaffold/README.md +6 -4
  6. package/bin/scaffold/src/hello-world.http.ts +7 -1
  7. package/build/browserless.d.ts +8 -5
  8. package/build/browserless.js +23 -22
  9. package/build/browsers/chrome.cdp.d.ts +2 -2
  10. package/build/browsers/chrome.cdp.js +2 -2
  11. package/build/browsers/chrome.playwright.d.ts +2 -2
  12. package/build/browsers/chrome.playwright.js +2 -2
  13. package/build/browsers/chromium.cdp.d.ts +4 -4
  14. package/build/browsers/chromium.cdp.js +49 -32
  15. package/build/browsers/chromium.playwright.d.ts +4 -4
  16. package/build/browsers/chromium.playwright.js +14 -13
  17. package/build/browsers/firefox.playwright.d.ts +4 -4
  18. package/build/browsers/firefox.playwright.js +14 -13
  19. package/build/browsers/index.d.ts +24 -8
  20. package/build/browsers/index.js +20 -15
  21. package/build/browsers/webkit.playwright.d.ts +4 -4
  22. package/build/browsers/webkit.playwright.js +14 -13
  23. package/build/config.d.ts +3 -0
  24. package/build/config.js +3 -0
  25. package/build/exports.d.ts +1 -0
  26. package/build/exports.js +1 -0
  27. package/build/file-system.d.ts +2 -3
  28. package/build/file-system.js +2 -2
  29. package/build/index.js +7 -7
  30. package/build/limiter.d.ts +2 -3
  31. package/build/limiter.js +11 -11
  32. package/build/logger.d.ts +19 -0
  33. package/build/logger.js +43 -0
  34. package/build/monitoring.d.ts +2 -3
  35. package/build/monitoring.js +4 -4
  36. package/build/router.d.ts +4 -5
  37. package/build/router.js +31 -28
  38. package/build/routes/chrome/http/content.post.body.json +8 -8
  39. package/build/routes/chrome/http/pdf.post.body.json +9 -9
  40. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  41. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  42. package/build/routes/chromium/http/content.post.body.json +8 -8
  43. package/build/routes/chromium/http/pdf.post.body.json +9 -9
  44. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  45. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  46. package/build/routes/firefox/ws/playwright.d.ts +2 -2
  47. package/build/routes/firefox/ws/playwright.js +1 -1
  48. package/build/routes/management/http/static.get.d.ts +2 -2
  49. package/build/routes/management/http/static.get.js +8 -10
  50. package/build/routes/management/tests/management.spec.js +9 -0
  51. package/build/routes/webkit/ws/playwright.d.ts +2 -2
  52. package/build/routes/webkit/ws/playwright.js +1 -1
  53. package/build/sdk-utils.js +23 -10
  54. package/build/server.d.ts +4 -5
  55. package/build/server.js +38 -33
  56. package/build/shared/browser.ws.d.ts +2 -2
  57. package/build/shared/browser.ws.js +1 -1
  58. package/build/shared/chromium.playwright.ws.d.ts +2 -2
  59. package/build/shared/chromium.playwright.ws.js +1 -1
  60. package/build/shared/chromium.ws.d.ts +2 -2
  61. package/build/shared/chromium.ws.js +1 -1
  62. package/build/shared/content.http.d.ts +2 -2
  63. package/build/shared/content.http.js +4 -1
  64. package/build/shared/download.http.d.ts +2 -2
  65. package/build/shared/download.http.js +11 -12
  66. package/build/shared/function.http.d.ts +2 -2
  67. package/build/shared/function.http.js +4 -5
  68. package/build/shared/json-protocol.http.d.ts +3 -3
  69. package/build/shared/json-protocol.http.js +2 -2
  70. package/build/shared/json-version.http.d.ts +3 -3
  71. package/build/shared/json-version.http.js +2 -2
  72. package/build/shared/page.ws.d.ts +2 -2
  73. package/build/shared/page.ws.js +1 -1
  74. package/build/shared/pdf.http.d.ts +2 -2
  75. package/build/shared/pdf.http.js +6 -4
  76. package/build/shared/performance.http.d.ts +2 -2
  77. package/build/shared/performance.http.js +2 -1
  78. package/build/shared/scrape.http.d.ts +2 -2
  79. package/build/shared/scrape.http.js +4 -1
  80. package/build/shared/screenshot.http.d.ts +2 -2
  81. package/build/shared/screenshot.http.js +4 -1
  82. package/build/shared/utils/function/handler.d.ts +2 -3
  83. package/build/shared/utils/function/handler.js +8 -8
  84. package/build/shared/utils/performance/child.js +4 -4
  85. package/build/shared/utils/performance/main.d.ts +1 -1
  86. package/build/shared/utils/performance/main.js +5 -7
  87. package/build/shared/utils/performance/types.d.ts +2 -1
  88. package/build/types.d.ts +6 -15
  89. package/build/types.js +1 -10
  90. package/build/utils.d.ts +1 -1
  91. package/build/utils.js +6 -2
  92. package/package.json +18 -15
  93. package/scripts/install-debugger.js +55 -15
  94. package/src/browserless.ts +29 -21
  95. package/src/browsers/chrome.cdp.ts +2 -5
  96. package/src/browsers/chrome.playwright.ts +2 -5
  97. package/src/browsers/chromium.cdp.ts +84 -35
  98. package/src/browsers/chromium.playwright.ts +26 -13
  99. package/src/browsers/firefox.playwright.ts +28 -13
  100. package/src/browsers/index.ts +24 -16
  101. package/src/browsers/webkit.playwright.ts +28 -13
  102. package/src/config.ts +4 -0
  103. package/src/exports.ts +1 -0
  104. package/src/file-system.ts +2 -7
  105. package/src/index.ts +7 -7
  106. package/src/limiter.ts +13 -11
  107. package/src/logger.ts +52 -0
  108. package/src/monitoring.ts +6 -8
  109. package/src/router.ts +29 -27
  110. package/src/routes/firefox/ws/playwright.ts +2 -0
  111. package/src/routes/management/http/static.get.ts +13 -10
  112. package/src/routes/management/tests/management.spec.ts +15 -0
  113. package/src/routes/webkit/ws/playwright.ts +2 -0
  114. package/src/sdk-utils.ts +20 -2
  115. package/src/server.ts +47 -32
  116. package/src/shared/browser.ws.ts +2 -0
  117. package/src/shared/chromium.playwright.ws.ts +2 -0
  118. package/src/shared/chromium.ws.ts +2 -0
  119. package/src/shared/content.http.ts +6 -0
  120. package/src/shared/download.http.ts +14 -11
  121. package/src/shared/function.http.ts +5 -4
  122. package/src/shared/json-protocol.http.ts +8 -3
  123. package/src/shared/json-version.http.ts +8 -4
  124. package/src/shared/page.ws.ts +2 -0
  125. package/src/shared/pdf.http.ts +7 -3
  126. package/src/shared/performance.http.ts +3 -0
  127. package/src/shared/scrape.http.ts +6 -0
  128. package/src/shared/screenshot.http.ts +5 -0
  129. package/src/shared/utils/function/handler.ts +9 -13
  130. package/src/shared/utils/performance/child.ts +4 -4
  131. package/src/shared/utils/performance/main.ts +5 -6
  132. package/src/shared/utils/performance/types.ts +2 -1
  133. package/src/types.ts +5 -9
  134. package/src/utils.ts +7 -2
  135. package/static/docs/swagger.json +11 -11
  136. package/static/docs/swagger.min.json +10 -10
  137. package/static/function/client.js +1656 -2916
  138. package/static/function/index.html +1656 -2916
@@ -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;
@@ -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,15 +7,16 @@ export class FirefoxPlaywright 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:firefox:playwright');
14
- constructor({ config, userDataDir, }) {
14
+ constructor({ config, userDataDir, logger, }) {
15
15
  super();
16
16
  this.userDataDir = userDataDir;
17
17
  this.config = config;
18
- this.debug(`Starting new browser instance`);
18
+ this.logger = logger;
19
+ this.logger.info(`Starting new ${this.constructor.name} instance`);
19
20
  }
20
21
  cleanListeners() {
21
22
  this.removeAllListeners();
@@ -23,7 +24,7 @@ export class FirefoxPlaywright extends EventEmitter {
23
24
  isRunning = () => this.running;
24
25
  close = async () => {
25
26
  if (this.browser) {
26
- this.debug(`Closing browser process and all listeners`);
27
+ this.logger.trace(`Closing ${this.constructor.name} process and all listeners`);
27
28
  this.emit('close');
28
29
  this.cleanListeners();
29
30
  this.browser.close();
@@ -34,16 +35,16 @@ export class FirefoxPlaywright extends EventEmitter {
34
35
  };
35
36
  pages = async () => [];
36
37
  getPageId = () => {
37
- throw new ServerError(`#getPageId is not yet supported with this browser.`);
38
+ throw new ServerError(`#getPageId is not yet supported with ${this.constructor.name}.`);
38
39
  };
39
40
  makeLiveURL = () => {
40
- throw new ServerError(`Live URLs are not yet supported with this browser.`);
41
+ throw new ServerError(`Live URLs are not yet supported with ${this.constructor.name}.`);
41
42
  };
42
43
  newPage = async () => {
43
- throw new ServerError(`Can't create new page with this browser`);
44
+ throw new ServerError(`Can't create new page with ${this.constructor.name}`);
44
45
  };
45
46
  launch = async (options = {}) => {
46
- this.debug(`Launching Playwright Handler`);
47
+ this.logger.info(`Launching ${this.constructor.name} Handler`);
47
48
  this.browser = await playwright.firefox.launchServer({
48
49
  ...options,
49
50
  args: [
@@ -53,7 +54,7 @@ export class FirefoxPlaywright extends EventEmitter {
53
54
  executablePath: playwright.firefox.executablePath(),
54
55
  });
55
56
  const browserWSEndpoint = this.browser.wsEndpoint();
56
- this.debug(`Browser is running on ${browserWSEndpoint}`);
57
+ this.logger.info(`${this.constructor.name} is running on ${browserWSEndpoint}`);
57
58
  this.browserWSEndpoint = browserWSEndpoint;
58
59
  this.running = true;
59
60
  return this.browser;
@@ -72,14 +73,14 @@ export class FirefoxPlaywright extends EventEmitter {
72
73
  return externalURL.href;
73
74
  };
74
75
  proxyPageWebSocket = async () => {
75
- console.warn(`Not yet implemented`);
76
+ this.logger.warn(`Not yet implemented in ${this.constructor.name}`);
76
77
  };
77
78
  proxyWebSocket = async (req, socket, head) => new Promise((resolve, reject) => {
78
79
  if (!this.browserWSEndpoint) {
79
80
  throw new ServerError(`No browserWSEndpoint found, did you launch first?`);
80
81
  }
81
82
  socket.once('close', resolve);
82
- this.debug(`Proxying ${req.parsed.href} to browser ${this.browserWSEndpoint}`);
83
+ this.logger.info(`Proxying ${req.parsed.href} to ${this.constructor.name} ${this.browserWSEndpoint}`);
83
84
  // Delete headers known to cause issues
84
85
  delete req.headers.origin;
85
86
  req.url = '';
@@ -87,7 +88,7 @@ export class FirefoxPlaywright extends EventEmitter {
87
88
  changeOrigin: true,
88
89
  target: this.browserWSEndpoint,
89
90
  }, (error) => {
90
- this.debug(`Error proxying session: ${error}`);
91
+ this.logger.error(`Error proxying session to ${this.constructor.name}: ${error}`);
91
92
  this.close();
92
93
  return reject(error);
93
94
  });
@@ -1,5 +1,4 @@
1
- /// <reference types="debug" />
2
- import { BrowserHTTPRoute, BrowserInstance, BrowserWebsocketRoute, BrowserlessSession, BrowserlessSessionJSON, CDPJSONPayload, ChromiumCDP, Config, Hooks, Request } from '@browserless.io/browserless';
1
+ import { BrowserHTTPRoute, BrowserInstance, BrowserServerOptions, BrowserWebsocketRoute, BrowserlessSession, BrowserlessSessionJSON, CDPJSONPayload, CDPLaunchOptions, ChromiumCDP, Config, Hooks, Logger, Request } from '@browserless.io/browserless';
3
2
  import { Page } from 'puppeteer-core';
4
3
  export declare class BrowserManager {
5
4
  protected config: Config;
@@ -7,11 +6,11 @@ export declare class BrowserManager {
7
6
  protected browsers: Map<BrowserInstance, BrowserlessSession>;
8
7
  protected launching: Map<string, Promise<unknown>>;
9
8
  protected timers: Map<string, number>;
10
- protected debug: import("debug").Debugger;
9
+ protected log: Logger;
11
10
  protected chromeBrowsers: (typeof ChromiumCDP)[];
12
11
  protected playwrightBrowserNames: string[];
13
12
  constructor(config: Config, hooks: Hooks);
14
- private browserIsChrome;
13
+ protected browserIsChrome: (b: BrowserInstance) => boolean;
15
14
  protected removeUserDataDir: (userDataDir: string | null) => Promise<void>;
16
15
  protected onNewPage: (req: Request, page: Page) => Promise<void>;
17
16
  /**
@@ -19,24 +18,41 @@ export declare class BrowserManager {
19
18
  * and modifies URLs to set them to the appropriate addresses configured.
20
19
  * When both Chrome and Chromium are installed, defaults to Chromium.
21
20
  */
22
- getProtocolJSON: () => Promise<object>;
21
+ getProtocolJSON: (logger: Logger) => Promise<object>;
23
22
  /**
24
23
  * Returns the /json/version API from Chromium or Chrome, whichever is installed,
25
24
  * and modifies URLs to set them to the appropriate addresses configured.
26
25
  * When both Chrome and Chromium are installed, defaults to Chromium.
27
26
  */
28
- getVersionJSON: () => Promise<CDPJSONPayload>;
27
+ getVersionJSON: (logger: Logger) => Promise<CDPJSONPayload>;
29
28
  /**
30
29
  * Returns a list of all Chrome-like browsers (both Chromium and Chrome) with
31
30
  * their respective /json/list contents. URLs are modified so that subsequent
32
31
  * calls can be forwarded to the appropriate destination
33
32
  */
34
33
  getJSONList: () => Promise<Array<CDPJSONPayload>>;
35
- private generateSessionJson;
34
+ protected generateSessionJson: (browser: BrowserInstance, session: BrowserlessSession) => Promise<{
35
+ browser: string;
36
+ browserId: string;
37
+ initialConnectURL: string;
38
+ killURL: string | null;
39
+ running: boolean;
40
+ timeAliveMs: number;
41
+ type: string;
42
+ id: string | null;
43
+ isTempDataDir: boolean;
44
+ launchOptions: CDPLaunchOptions | BrowserServerOptions;
45
+ numbConnected: number;
46
+ resolver: (val: unknown) => void;
47
+ routePath: string | string[];
48
+ startedOn: number;
49
+ ttl: number;
50
+ userDataDir: string | null;
51
+ }[]>;
36
52
  close: (browser: BrowserInstance, session: BrowserlessSession) => Promise<void>;
37
53
  getAllSessions: () => Promise<BrowserlessSessionJSON[]>;
38
54
  complete: (browser: BrowserInstance) => Promise<void>;
39
- getBrowserForRequest: (req: Request, router: BrowserHTTPRoute | BrowserWebsocketRoute) => Promise<BrowserInstance>;
55
+ getBrowserForRequest: (req: Request, router: BrowserHTTPRoute | BrowserWebsocketRoute, logger: Logger) => Promise<BrowserInstance>;
40
56
  shutdown: () => Promise<void>;
41
57
  /**
42
58
  * Left blank for downstream SDK modules to optionally implement.
@@ -1,4 +1,4 @@
1
- import { BLESS_PAGE_IDENTIFIER, BadRequest, ChromeCDP, ChromePlaywright, ChromiumCDP, ChromiumPlaywright, FirefoxPlaywright, HTTPManagementRoutes, NotFound, ServerError, WebkitPlaywright, availableBrowsers, convertIfBase64, createLogger, exists, generateDataDir, makeExternalURL, noop, parseBooleanParam, } from '@browserless.io/browserless';
1
+ import { BLESS_PAGE_IDENTIFIER, BadRequest, ChromeCDP, ChromePlaywright, ChromiumCDP, ChromiumPlaywright, FirefoxPlaywright, HTTPManagementRoutes, Logger, NotFound, ServerError, WebkitPlaywright, availableBrowsers, convertIfBase64, exists, generateDataDir, makeExternalURL, noop, parseBooleanParam, } from '@browserless.io/browserless';
2
2
  import { deleteAsync } from 'del';
3
3
  import path from 'path';
4
4
  export class BrowserManager {
@@ -7,7 +7,7 @@ export class BrowserManager {
7
7
  browsers = new Map();
8
8
  launching = new Map();
9
9
  timers = new Map();
10
- debug = createLogger('browser-manager');
10
+ log = new Logger('browser-manager');
11
11
  chromeBrowsers = [ChromiumCDP, ChromeCDP];
12
12
  playwrightBrowserNames = [
13
13
  ChromiumPlaywright.name,
@@ -22,9 +22,9 @@ export class BrowserManager {
22
22
  browserIsChrome = (b) => this.chromeBrowsers.some((chromeBrowser) => b instanceof chromeBrowser);
23
23
  removeUserDataDir = async (userDataDir) => {
24
24
  if (userDataDir && (await exists(userDataDir))) {
25
- this.debug(`Deleting data directory "${userDataDir}"`);
25
+ this.log.info(`Deleting data directory "${userDataDir}"`);
26
26
  await deleteAsync(userDataDir, { force: true }).catch((err) => {
27
- this.debug(`Error cleaning up user-data-dir "${err}" at ${userDataDir}`);
27
+ this.log.error(`Error cleaning up user-data-dir "${err}" at ${userDataDir}`);
28
28
  });
29
29
  }
30
30
  };
@@ -36,7 +36,7 @@ export class BrowserManager {
36
36
  * and modifies URLs to set them to the appropriate addresses configured.
37
37
  * When both Chrome and Chromium are installed, defaults to Chromium.
38
38
  */
39
- getProtocolJSON = async () => {
39
+ getProtocolJSON = async (logger) => {
40
40
  const Browser = (await availableBrowsers).find((InstalledBrowser) => this.chromeBrowsers.some((ChromeBrowser) => InstalledBrowser === ChromeBrowser));
41
41
  if (!Browser) {
42
42
  throw new Error(`No Chrome or Chromium browsers are installed!`);
@@ -44,6 +44,7 @@ export class BrowserManager {
44
44
  const browser = new Browser({
45
45
  blockAds: false,
46
46
  config: this.config,
47
+ logger,
47
48
  userDataDir: null,
48
49
  });
49
50
  await browser.launch();
@@ -62,8 +63,8 @@ export class BrowserManager {
62
63
  * and modifies URLs to set them to the appropriate addresses configured.
63
64
  * When both Chrome and Chromium are installed, defaults to Chromium.
64
65
  */
65
- getVersionJSON = async () => {
66
- this.debug(`Launching Chromium to generate /json/version results`);
66
+ getVersionJSON = async (logger) => {
67
+ this.log.info(`Launching Chromium to generate /json/version results`);
67
68
  const Browser = (await availableBrowsers).find((InstalledBrowser) => this.chromeBrowsers.some((ChromeBrowser) => InstalledBrowser === ChromeBrowser));
68
69
  if (!Browser) {
69
70
  throw new ServerError(`No Chrome or Chromium browsers are installed!`);
@@ -71,6 +72,7 @@ export class BrowserManager {
71
72
  const browser = new Browser({
72
73
  blockAds: false,
73
74
  config: this.config,
75
+ logger,
74
76
  userDataDir: null,
75
77
  });
76
78
  await browser.launch();
@@ -178,15 +180,15 @@ export class BrowserManager {
178
180
  };
179
181
  close = async (browser, session) => {
180
182
  const cleanupACtions = [];
181
- this.debug(`${session.numbConnected} Client(s) are currently connected`);
183
+ this.log.info(`${session.numbConnected} Client(s) are currently connected`);
182
184
  // Don't close if there's clients still connected
183
185
  if (session.numbConnected > 0) {
184
186
  return;
185
187
  }
186
- this.debug(`Closing browser session`);
188
+ this.log.info(`Closing browser session`);
187
189
  cleanupACtions.push(() => browser.close());
188
190
  if (session.isTempDataDir) {
189
- this.debug(`Deleting "${session.userDataDir}" user-data-dir and session from memory`);
191
+ this.log.info(`Deleting "${session.userDataDir}" user-data-dir and session from memory`);
190
192
  this.browsers.delete(browser);
191
193
  cleanupACtions.push(() => this.removeUserDataDir(session.userDataDir));
192
194
  }
@@ -204,7 +206,7 @@ export class BrowserManager {
204
206
  complete = async (browser) => {
205
207
  const session = this.browsers.get(browser);
206
208
  if (!session) {
207
- this.debug(`Couldn't locate session for browser, proceeding with close`);
209
+ this.log.info(`Couldn't locate session for browser, proceeding with close`);
208
210
  return browser.close();
209
211
  }
210
212
  const { id, resolver } = session;
@@ -215,7 +217,7 @@ export class BrowserManager {
215
217
  --session.numbConnected;
216
218
  this.close(browser, session);
217
219
  };
218
- getBrowserForRequest = async (req, router) => {
220
+ getBrowserForRequest = async (req, router, logger) => {
219
221
  const { browser: Browser } = router;
220
222
  const blockAds = parseBooleanParam(req.parsed.searchParams, 'blockAds', false);
221
223
  const decodedLaunchOptions = convertIfBase64(req.parsed.searchParams.get('launch') || '{}');
@@ -228,7 +230,7 @@ export class BrowserManager {
228
230
  if (found) {
229
231
  const [browser, session] = found;
230
232
  ++session.numbConnected;
231
- this.debug(`Located browser with ID ${id}`);
233
+ this.log.debug(`Located browser with ID ${id}`);
232
234
  return browser;
233
235
  }
234
236
  throw new NotFound(`Couldn't locate browser "${id}" for request "${req.parsed.pathname}"`);
@@ -259,6 +261,8 @@ export class BrowserManager {
259
261
  }));
260
262
  const found = allPages.flat().find((b) => b.id === id);
261
263
  if (found) {
264
+ const session = this.browsers.get(found.browser);
265
+ ++session.numbConnected;
262
266
  return found.browser;
263
267
  }
264
268
  throw new NotFound(`Couldn't locate browser "${id}" for request "${req.parsed.pathname}"`);
@@ -299,6 +303,7 @@ export class BrowserManager {
299
303
  const browser = new Browser({
300
304
  blockAds,
301
305
  config: this.config,
306
+ logger,
302
307
  userDataDir,
303
308
  });
304
309
  const connectionMeta = {
@@ -323,7 +328,7 @@ export class BrowserManager {
323
328
  return browser;
324
329
  };
325
330
  shutdown = async () => {
326
- this.debug(`Closing down browser instances`);
331
+ this.log.info(`Closing down browser instances`);
327
332
  const sessions = Array.from(this.browsers);
328
333
  await Promise.all(sessions.map(([b]) => b.close()));
329
334
  const timers = Array.from(this.timers);
@@ -332,7 +337,7 @@ export class BrowserManager {
332
337
  this.browsers = new Map();
333
338
  this.timers = new Map();
334
339
  await this.stop();
335
- this.debug(`Shutdown complete`);
340
+ this.log.info(`Shutdown complete`);
336
341
  };
337
342
  /**
338
343
  * Left blank for downstream SDK modules to optionally implement.
@@ -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';
@@ -15,9 +14,10 @@ export declare class WebkitPlaywright extends EventEmitter {
15
14
  protected proxy: httpProxy<import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>>;
16
15
  protected browser: playwright.BrowserServer | null;
17
16
  protected browserWSEndpoint: string | null;
18
- protected debug: import("debug").Debugger;
19
- constructor({ config, userDataDir, }: {
17
+ protected logger: Logger;
18
+ constructor({ config, userDataDir, logger, }: {
20
19
  config: Config;
20
+ logger: Logger;
21
21
  userDataDir: WebkitPlaywright['userDataDir'];
22
22
  });
23
23
  protected cleanListeners(): void;