@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 { APITags, BadRequest, BrowserHTTPRoute, BrowserlessRoutes, ChromiumCDP, HTTPRoutes, Methods, bestAttemptCatch, contentTypes, dedent, noop, sleep, waitForEvent as waitForEvt, waitForFunction as waitForFn, writeResponse, } from '@browserless.io/browserless';
1
+ import { APITags, BadRequest, BrowserHTTPRoute, BrowserlessRoutes, ChromiumCDP, HTTPRoutes, Methods, bestAttemptCatch, contentTypes, dedent, noop, sleep, waitForEvent as waitForEvt, waitForFunction as waitForFn, } from '@browserless.io/browserless';
2
2
  import { Stream } from 'stream';
3
3
  export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
4
4
  name = BrowserlessRoutes.ChromiumPDFPostRoute;
@@ -16,13 +16,13 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
16
16
  method = Methods.post;
17
17
  path = [HTTPRoutes.pdf, HTTPRoutes.chromiumPdf];
18
18
  tags = [APITags.browserAPI];
19
- handler = async (req, res, browser) => {
19
+ handler = async (req, res, logger, browser) => {
20
+ logger.info('PDF API invoked with body:', req.body);
20
21
  const contentType = !req.headers.accept || req.headers.accept?.includes('*')
21
22
  ? 'application/pdf'
22
23
  : req.headers.accept;
23
24
  if (!req.body) {
24
- writeResponse(res, 400, `Couldn't parse JSON body`);
25
- return;
25
+ throw new BadRequest(`Couldn't parse JSON body`);
26
26
  }
27
27
  res.setHeader('Content-Type', contentType);
28
28
  const { url, gotoOptions, authenticate, html, addScriptTag = [], addStyleTag = [], cookies = [], emulateMediaType, rejectRequestPattern = [], requestInterceptors = [], rejectResourceTypes = [], options, setExtraHTTPHeaders, setJavaScriptEnabled, userAgent, viewport, waitForEvent, waitForFunction, waitForSelector, waitForTimeout, bestAttempt = false, } = req.body;
@@ -60,6 +60,7 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
60
60
  page.on('request', (req) => {
61
61
  if (!!rejectRequestPattern.find((pattern) => req.url().match(pattern)) ||
62
62
  rejectResourceTypes.includes(req.resourceType())) {
63
+ logger.debug(`Aborting request ${req.method()}: ${req.url()}`);
63
64
  return req.abort();
64
65
  }
65
66
  const interceptor = requestInterceptors.find((r) => req.url().match(r.pattern));
@@ -112,5 +113,6 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
112
113
  readStream.end(pdfBuffer);
113
114
  await new Promise((r) => readStream.pipe(res).once('close', r));
114
115
  page.close().catch(noop);
116
+ logger.info('PDF API request completed');
115
117
  };
116
118
  }
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { APITags, BrowserHTTPRoute, BrowserInstance, CDPLaunchOptions, ChromiumCDP, HTTPRoutes, Methods, Request, SystemQueryParameters, contentTypes } from '@browserless.io/browserless';
2
+ import { APITags, BrowserHTTPRoute, BrowserInstance, CDPLaunchOptions, ChromiumCDP, HTTPRoutes, Logger, Methods, Request, SystemQueryParameters, contentTypes } from '@browserless.io/browserless';
3
3
  import { ServerResponse } from 'http';
4
4
  export interface BodySchema {
5
5
  budgets?: Array<object>;
@@ -26,5 +26,5 @@ export default class PerformancePost extends BrowserHTTPRoute {
26
26
  method: Methods;
27
27
  path: HTTPRoutes[];
28
28
  tags: APITags[];
29
- handler: (req: Request, res: ServerResponse, browser: BrowserInstance) => Promise<void>;
29
+ handler: (req: Request, res: ServerResponse, _logger: Logger, browser: BrowserInstance) => Promise<void>;
30
30
  }
@@ -11,11 +11,12 @@ export default class PerformancePost extends BrowserHTTPRoute {
11
11
  method = Methods.post;
12
12
  path = [HTTPRoutes.performance, HTTPRoutes.chromiumPerformance];
13
13
  tags = [APITags.browserAPI];
14
- handler = async (req, res, browser) => {
14
+ handler = async (req, res, _logger, browser) => {
15
15
  const config = this.config();
16
16
  const response = await main({
17
17
  browser,
18
18
  context: req.body,
19
+ logger: _logger,
19
20
  timeout: config.getTimeout(),
20
21
  });
21
22
  return jsonResponse(res, 200, response);
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { APITags, BrowserHTTPRoute, BrowserInstance, CDPLaunchOptions, ChromiumCDP, HTTPRoutes, InBoundRequest, Methods, OutBoundRequest, Request, ScrapeDebugOptions, ScrapeElementSelector, SystemQueryParameters, WaitForEventOptions, WaitForFunctionOptions, WaitForSelectorOptions, bestAttempt, contentTypes, rejectRequestPattern, rejectResourceTypes, requestInterceptors } from '@browserless.io/browserless';
2
+ import { APITags, BrowserHTTPRoute, BrowserInstance, CDPLaunchOptions, ChromiumCDP, HTTPRoutes, InBoundRequest, Logger, Methods, OutBoundRequest, Request, ScrapeDebugOptions, ScrapeElementSelector, SystemQueryParameters, WaitForEventOptions, WaitForFunctionOptions, WaitForSelectorOptions, bestAttempt, contentTypes, rejectRequestPattern, rejectResourceTypes, requestInterceptors } from '@browserless.io/browserless';
3
3
  import { Cookie, Page } from 'puppeteer-core';
4
4
  import { ServerResponse } from 'http';
5
5
  export interface BodySchema {
@@ -115,5 +115,5 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute {
115
115
  method: Methods;
116
116
  path: HTTPRoutes[];
117
117
  tags: APITags[];
118
- handler: (req: Request, res: ServerResponse, browser: BrowserInstance) => Promise<void>;
118
+ handler: (req: Request, res: ServerResponse, logger: Logger, browser: BrowserInstance) => Promise<void>;
119
119
  }
@@ -54,7 +54,8 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute {
54
54
  method = Methods.post;
55
55
  path = [HTTPRoutes.scrape, HTTPRoutes.chromiumScrape];
56
56
  tags = [APITags.browserAPI];
57
- handler = async (req, res, browser) => {
57
+ handler = async (req, res, logger, browser) => {
58
+ logger.info('Scrape API invoked with body:', req.body);
58
59
  const contentType = !req.headers.accept || req.headers.accept?.includes('*')
59
60
  ? contentTypes.html
60
61
  : req.headers.accept;
@@ -121,6 +122,7 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute {
121
122
  page.on('request', (req) => {
122
123
  if (!!rejectRequestPattern.find((pattern) => req.url().match(pattern)) ||
123
124
  rejectResourceTypes.includes(req.resourceType())) {
125
+ logger.debug(`Aborting request ${req.method()}: ${req.url()}`);
124
126
  return req.abort();
125
127
  }
126
128
  const interceptor = requestInterceptors.find((r) => req.url().match(r.pattern));
@@ -198,6 +200,7 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute {
198
200
  debug: debugData,
199
201
  };
200
202
  page.close().catch(noop);
203
+ logger.info('Scrape API request completed');
201
204
  return jsonResponse(res, 200, response, false);
202
205
  };
203
206
  }
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { APITags, BrowserHTTPRoute, BrowserInstance, CDPLaunchOptions, ChromiumCDP, HTTPRoutes, Methods, Request, SystemQueryParameters, WaitForEventOptions, WaitForFunctionOptions, WaitForSelectorOptions, bestAttempt, contentTypes, rejectRequestPattern, rejectResourceTypes, requestInterceptors } from '@browserless.io/browserless';
2
+ import { APITags, BrowserHTTPRoute, BrowserInstance, CDPLaunchOptions, ChromiumCDP, HTTPRoutes, Logger, Methods, Request, SystemQueryParameters, WaitForEventOptions, WaitForFunctionOptions, WaitForSelectorOptions, bestAttempt, contentTypes, rejectRequestPattern, rejectResourceTypes, requestInterceptors } from '@browserless.io/browserless';
3
3
  import { Page } from 'puppeteer-core';
4
4
  import { ServerResponse } from 'http';
5
5
  export interface QuerySchema extends SystemQueryParameters {
@@ -46,5 +46,5 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
46
46
  method: Methods;
47
47
  path: HTTPRoutes[];
48
48
  tags: APITags[];
49
- handler: (req: Request, res: ServerResponse, browser: BrowserInstance) => Promise<void>;
49
+ handler: (req: Request, res: ServerResponse, logger: Logger, browser: BrowserInstance) => Promise<void>;
50
50
  }
@@ -15,7 +15,8 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
15
15
  method = Methods.post;
16
16
  path = [HTTPRoutes.screenshot, HTTPRoutes.chromiumScreenshot];
17
17
  tags = [APITags.browserAPI];
18
- handler = async (req, res, browser) => {
18
+ handler = async (req, res, logger, browser) => {
19
+ logger.info('Screenshot API invoked with body:', req.body);
19
20
  const contentType = !req.headers.accept || req.headers.accept?.includes('*')
20
21
  ? 'image/png'
21
22
  : req.headers.accept;
@@ -61,6 +62,7 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
61
62
  page.on('request', (req) => {
62
63
  if (!!rejectRequestPattern.find((pattern) => req.url().match(pattern)) ||
63
64
  rejectResourceTypes.includes(req.resourceType())) {
65
+ logger.debug(`Aborting request ${req.method()}: ${req.url()}`);
64
66
  return req.abort();
65
67
  }
66
68
  const interceptor = requestInterceptors.find((r) => req.url().match(r.pattern));
@@ -122,5 +124,6 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
122
124
  readStream.end(buffer);
123
125
  await new Promise((r) => readStream.pipe(res).once('close', r));
124
126
  page.close().catch(noop);
127
+ logger.info('Screenshot API request completed');
125
128
  };
126
129
  }
@@ -1,7 +1,6 @@
1
- import { BrowserInstance, Config, Request } from '@browserless.io/browserless';
1
+ import { BrowserInstance, Config, Logger, Request } from '@browserless.io/browserless';
2
2
  import { FunctionRunner } from './client.js';
3
3
  import { Page } from 'puppeteer-core';
4
- import debug from 'debug';
5
4
  declare global {
6
5
  interface Window {
7
6
  BrowserlessFunctionRunner: typeof FunctionRunner;
@@ -10,7 +9,7 @@ declare global {
10
9
  interface HandlerOptions {
11
10
  downloadPath?: string;
12
11
  }
13
- declare const _default: (config: Config, debug: debug.Debugger, options?: HandlerOptions) => (req: Request, browser: BrowserInstance) => Promise<{
12
+ declare const _default: (config: Config, logger: Logger, options?: HandlerOptions) => (req: Request, browser: BrowserInstance) => Promise<{
14
13
  contentType: string;
15
14
  page: Page;
16
15
  payload: unknown;
@@ -1,7 +1,7 @@
1
1
  import { BadRequest, HTTPRoutes, contentTypes, convertIfBase64, exists, getTokenFromRequest, id, makeExternalURL, mimeTypes, } from '@browserless.io/browserless';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
- export default (config, debug, options = {}) => async (req, browser) => {
4
+ export default (config, logger, options = {}) => async (req, browser) => {
5
5
  const isJson = req.headers['content-type']?.includes('json');
6
6
  const functionAssetLocation = path.join(config.getStatic(), 'function');
7
7
  const functionRequestPath = makeExternalURL(config.getExternalAddress(), HTTPRoutes.function);
@@ -29,7 +29,7 @@ export default (config, debug, options = {}) => async (req, browser) => {
29
29
  */
30
30
  page.on('request', async (request) => {
31
31
  const requestUrl = request.url();
32
- debug(`Outbound Page Request: "${requestUrl}"`);
32
+ logger.info(`Outbound Page Request: "${requestUrl}"`);
33
33
  if (requestUrl.startsWith(functionRequestPath)) {
34
34
  const filename = path.basename(requestUrl);
35
35
  if (filename === functionCodeJS) {
@@ -48,23 +48,23 @@ export default (config, debug, options = {}) => async (req, browser) => {
48
48
  status: 200,
49
49
  });
50
50
  }
51
- debug(`Static asset request to "${requestUrl}" couldn't be found, 404-ing`);
51
+ logger.warn(`Static asset request to "${requestUrl}" couldn't be found, 404-ing`);
52
52
  return request.respond({
53
53
  body: code,
54
54
  contentType: `Couldn't locate this file "${filename}" request "${requestUrl}" in "${functionAssetLocation}"`,
55
55
  status: 404,
56
56
  });
57
57
  }
58
- debug(`Request: "${requestUrl}" no responder found, continuing...`);
58
+ logger.info(`Request: "${requestUrl}" no responder found, continuing...`);
59
59
  return request.continue();
60
60
  });
61
61
  page.on('response', (res) => {
62
- if (res.status() !== 200) {
63
- debug(`Received a non-200 response for request "${res.url()}"`);
62
+ if (!res.ok()) {
63
+ logger.warn(`Received a non-200 response for request "${res.url()}"`);
64
64
  }
65
65
  });
66
66
  page.on('console', (event) => {
67
- debug(`${event.type()}: ${event.text()}`);
67
+ logger.trace(`${event.type()}: ${event.text()}`);
68
68
  });
69
69
  await page.goto(functionIndexHTML);
70
70
  const { contentType, payload } = await page
@@ -85,7 +85,7 @@ export default (config, debug, options = {}) => async (req, browser) => {
85
85
  });
86
86
  }, browserWSEndpoint, context, functionCodeJS, JSON.stringify(options))
87
87
  .catch((e) => {
88
- debug(`Error running code: ${e}`);
88
+ logger.error(`Error running code: ${e}`);
89
89
  throw new BadRequest(e.message);
90
90
  });
91
91
  return {
@@ -1,11 +1,11 @@
1
- import { createLogger } from '@browserless.io/browserless';
1
+ import { Logger } from '@browserless.io/browserless';
2
2
  import lighthouse from 'lighthouse';
3
- const debug = createLogger('http:performance:child');
4
- debug(`Child init`);
3
+ const logger = new Logger('http:performance:child');
4
+ logger.info(`Child init`);
5
5
  const send = (msg) => process.send && process.send(msg);
6
6
  const start = async ({ url, config, options }) => {
7
7
  try {
8
- debug(`Child got payload, starting lighthouse`);
8
+ logger.info(`Child got payload, starting lighthouse`);
9
9
  const results = await lighthouse(url, options, config);
10
10
  send({
11
11
  data: results?.lhr,
@@ -1,3 +1,3 @@
1
1
  import { mainOptions } from './types.js';
2
- declare const _default: ({ browser, context, timeout, }: mainOptions) => Promise<unknown>;
2
+ declare const _default: ({ browser, context, logger, timeout, }: mainOptions) => Promise<unknown>;
3
3
  export default _default;
@@ -1,14 +1,12 @@
1
- import { createLogger } from '@browserless.io/browserless';
2
1
  import { fork } from 'child_process';
3
2
  import path from 'path';
4
3
  const DEFAULT_AUDIT_CONFIG = {
5
4
  extends: 'lighthouse:default',
6
5
  };
7
- export default async ({ browser, context, timeout, }) => {
6
+ export default async ({ browser, context, logger, timeout, }) => {
8
7
  return new Promise((resolve, reject) => {
9
- const debug = createLogger('http:performance:main');
10
8
  const childPath = path.join('./', 'build', 'shared', 'utils', 'performance', 'child.js');
11
- debug(`Starting up child at ${childPath}`);
9
+ logger.trace(`Starting up child at ${childPath}`);
12
10
  const child = fork(childPath);
13
11
  const port = new URL(browser.wsEndpoint() || '').port;
14
12
  let closed = false;
@@ -34,13 +32,13 @@ export default async ({ browser, context, timeout, }) => {
34
32
  port,
35
33
  };
36
34
  child.on('error', (err) => {
37
- debug(`Error in child process`, err);
35
+ logger.error(`Error in child process`, err);
38
36
  reject('Performance run error: ' + err.message);
39
37
  close(child.pid);
40
38
  });
41
39
  child.on('message', (payload) => {
42
40
  if (payload.event === 'created') {
43
- debug(`Child process is up, sending performance request`);
41
+ logger.info(`Child process is up, sending performance request`);
44
42
  return child.send({
45
43
  config,
46
44
  event: 'start',
@@ -49,7 +47,7 @@ export default async ({ browser, context, timeout, }) => {
49
47
  });
50
48
  }
51
49
  if (payload.event === 'complete') {
52
- debug(`Performance gathered, closing and resolving request`);
50
+ logger.info(`Performance gathered, closing and resolving request`);
53
51
  close(child.pid);
54
52
  return resolve({
55
53
  data: payload.data,
@@ -1,5 +1,5 @@
1
+ import { BrowserInstance, Logger } from '@browserless.io/browserless';
1
2
  import { Config, Flags } from 'lighthouse';
2
- import { BrowserInstance } from '@browserless.io/browserless';
3
3
  export interface Message {
4
4
  data?: unknown;
5
5
  error?: unknown;
@@ -12,6 +12,7 @@ export interface mainOptions {
12
12
  config?: unknown;
13
13
  url: string;
14
14
  };
15
+ logger: Logger;
15
16
  timeout: number;
16
17
  }
17
18
  export interface start {
package/build/types.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
3
  /// <reference types="node" />
4
- /// <reference types="debug" />
5
4
  import * as http from 'http';
6
5
  import * as stream from 'stream';
7
- import { APITags, Browserless, ChromiumCDP, ChromiumPlaywright, Config, FirefoxPlaywright, HTTPManagementRoutes, HTTPRoutes, Methods, Metrics, Request, WebkitPlaywright, WebsocketRoutes, contentTypes } from '@browserless.io/browserless';
6
+ import { APITags, Browserless, ChromiumCDP, ChromiumPlaywright, Config, FirefoxPlaywright, HTTPManagementRoutes, HTTPRoutes, Logger, Methods, Metrics, Request, WebkitPlaywright, WebsocketRoutes, contentTypes } from '@browserless.io/browserless';
8
7
  import { HTTPRequest, Page, ResponseForRequest, ScreenshotOptions } from 'puppeteer-core';
9
8
  export type PathTypes = HTTPRoutes | WebsocketRoutes | HTTPManagementRoutes | string;
10
9
  export interface BeforeRequest {
@@ -52,11 +51,10 @@ declare abstract class Route {
52
51
  protected _browserManager: Browserless['browserManager'];
53
52
  protected _config: Browserless['config'];
54
53
  protected _fileSystem: Browserless['fileSystem'];
55
- protected _debug: Browserless['debug'];
56
54
  protected _metrics: Browserless['metrics'];
57
55
  protected _monitoring: Browserless['monitoring'];
58
56
  protected _staticSDKDir: Browserless['staticSDKDir'];
59
- constructor(_browserManager: Browserless['browserManager'], _config: Browserless['config'], _fileSystem: Browserless['fileSystem'], _debug: Browserless['debug'], _metrics: Browserless['metrics'], _monitoring: Browserless['monitoring'], _staticSDKDir: Browserless['staticSDKDir']);
57
+ constructor(_browserManager: Browserless['browserManager'], _config: Browserless['config'], _fileSystem: Browserless['fileSystem'], _metrics: Browserless['metrics'], _monitoring: Browserless['monitoring'], _staticSDKDir: Browserless['staticSDKDir']);
60
58
  /**
61
59
  * A unique name to identify this route. Used in downstream
62
60
  * SDKs to potentially remove or disable.
@@ -107,13 +105,6 @@ declare abstract class Route {
107
105
  * @returns Config
108
106
  */
109
107
  config: () => Config;
110
- /**
111
- * Helper function that loads the debug module, useful
112
- * for logging messages scoped to the routes path. Defined
113
- * and injected by browserless after initialization.
114
- * @returns Debug
115
- */
116
- debug: () => import("debug").Debugger;
117
108
  /**
118
109
  * Helper function that loads the file-system module
119
110
  * for interacting with file-systems. Defined and injected by
@@ -184,7 +175,7 @@ export declare abstract class HTTPRoute extends BasicHTTPRoute {
184
175
  /**
185
176
  * Handles an inbound HTTP request, and supplies the Request and Response objects from node's HTTP request event
186
177
  */
187
- abstract handler: (req: Request, res: http.ServerResponse) => Promise<unknown>;
178
+ abstract handler: (req: Request, res: http.ServerResponse, logger: Logger) => Promise<unknown>;
188
179
  }
189
180
  /**
190
181
  * A HTTP-based route, with a handler, that can fulfill requests but
@@ -198,7 +189,7 @@ export declare abstract class BrowserHTTPRoute extends BasicHTTPRoute {
198
189
  * Handles an inbound HTTP request with a 3rd param of the predefined
199
190
  * browser used for the route -- only Chrome CDP is support currently.
200
191
  */
201
- abstract handler: (req: Request, res: http.ServerResponse, browser: BrowserInstance) => Promise<unknown>;
192
+ abstract handler: (req: Request, res: http.ServerResponse, logger: Logger, browser: BrowserInstance) => Promise<unknown>;
202
193
  /**
203
194
  * An optional function to automatically set up or handle new page
204
195
  * creation. Useful for injecting behaviors or other functionality.
@@ -214,7 +205,7 @@ export declare abstract class WebSocketRoute extends Route {
214
205
  /**
215
206
  * Handles an inbound Websocket request, and handles the connection
216
207
  */
217
- abstract handler: (req: Request, socket: stream.Duplex, head: Buffer) => Promise<unknown>;
208
+ abstract handler: (req: Request, socket: stream.Duplex, head: Buffer, logger: Logger) => Promise<unknown>;
218
209
  }
219
210
  /**
220
211
  * A WebSocket-based route, with a handler, that can fulfill requests
@@ -228,7 +219,7 @@ export declare abstract class BrowserWebsocketRoute extends Route {
228
219
  * Handles an inbound Websocket request, and handles the connection
229
220
  * with the prior set browser being injected.
230
221
  */
231
- abstract handler(req: Request, socket: stream.Duplex, head: Buffer, browser: BrowserInstance): Promise<unknown>;
222
+ abstract handler(req: Request, socket: stream.Duplex, head: Buffer, logger: Logger, browser: BrowserInstance): Promise<unknown>;
232
223
  /**
233
224
  * An optional function to automatically set up or handle new page
234
225
  * creation. Useful for injecting behaviors or other functionality.
package/build/types.js CHANGED
@@ -2,15 +2,13 @@ class Route {
2
2
  _browserManager;
3
3
  _config;
4
4
  _fileSystem;
5
- _debug;
6
5
  _metrics;
7
6
  _monitoring;
8
7
  _staticSDKDir;
9
- constructor(_browserManager, _config, _fileSystem, _debug, _metrics, _monitoring, _staticSDKDir) {
8
+ constructor(_browserManager, _config, _fileSystem, _metrics, _monitoring, _staticSDKDir) {
10
9
  this._browserManager = _browserManager;
11
10
  this._config = _config;
12
11
  this._fileSystem = _fileSystem;
13
- this._debug = _debug;
14
12
  this._metrics = _metrics;
15
13
  this._monitoring = _monitoring;
16
14
  this._staticSDKDir = _staticSDKDir;
@@ -60,13 +58,6 @@ class Route {
60
58
  * @returns Config
61
59
  */
62
60
  config = () => this._config;
63
- /**
64
- * Helper function that loads the debug module, useful
65
- * for logging messages scoped to the routes path. Defined
66
- * and injected by browserless after initialization.
67
- * @returns Debug
68
- */
69
- debug = () => this._debug;
70
61
  /**
71
62
  * Helper function that loads the file-system module
72
63
  * for interacting with file-systems. Defined and injected by
package/build/utils.d.ts CHANGED
@@ -120,6 +120,6 @@ interface RequestInitTimeout extends RequestInit {
120
120
  }
121
121
  export declare const fetchTimeout: (input: RequestInfo | URL, initWithTimeout?: RequestInitTimeout) => Promise<Response>;
122
122
  export declare const untildify: (path: string) => string;
123
- export declare const printLogo: (docsLink: string) => string;
123
+ export declare const printLogo: (docsLink: string, debugURL?: string | boolean) => string;
124
124
  export declare const getCDPClient: (page: Page) => CDPSession;
125
125
  export {};
package/build/utils.js CHANGED
@@ -561,12 +561,16 @@ export const untildify = (path) => {
561
561
  const homeDir = homedir();
562
562
  return homeDir ? path.replace(/^~(?=$|\/|\\)/, homeDir) : path;
563
563
  };
564
- export const printLogo = (docsLink) => `
564
+ export const printLogo = (docsLink, debugURL) => `
565
565
  ---------------------------------------------------------
566
566
  | browserless.io
567
567
  | To read documentation and more, load in your browser:
568
568
  |
569
- | ${docsLink}
569
+ | OpenAPI: ${docsLink}
570
+ | Full Documentation: https://docs.browserless.io/ ${
571
+ /*prettier-ignore*/
572
+ debugURL ? `
573
+ | Debbuger: ${debugURL}` : ""}
570
574
  ---------------------------------------------------------
571
575
  ${gradient('#ff1a8c', '#ffea00')(`
572
576
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserless.io/browserless",
3
- "version": "2.7.1",
3
+ "version": "2.9.0",
4
4
  "license": "SSPL",
5
5
  "description": "The browserless platform",
6
6
  "author": "browserless.io",
@@ -25,8 +25,9 @@
25
25
  "clean": "node scripts/clean.js",
26
26
  "dev": "npm run build:dev && env-cmd -f .env node build",
27
27
  "install:adblock": "node scripts/install-adblock.js",
28
+ "install:debugger": "node scripts/install-debugger.js",
28
29
  "install:browsers": "npx --yes playwright install chromium firefox webkit chrome",
29
- "install:dev": "npm run install:browsers",
30
+ "install:dev": "npm run install:browsers && npm run install:debugger",
30
31
  "lint": "eslint . --ext .ts --fix",
31
32
  "prepack": "npm run build:dev",
32
33
  "prettier": "prettier '{src,functions,scripts,bin,external,.github}/**/*.{js,ts,json,yml,yaml,md}' --log-level error --write",
@@ -55,40 +56,42 @@
55
56
  "get-port": "^7.1.0",
56
57
  "gradient-string": "^2.0.0",
57
58
  "http-proxy": "^1.18.1",
58
- "lighthouse": "^11.1.0",
59
+ "lighthouse": "^12.0.0",
59
60
  "micromatch": "^4.0.4",
60
- "playwright-core": "1.43.0",
61
- "puppeteer-core": "^22.6.3",
61
+ "playwright-core": "1.44.0",
62
+ "puppeteer-core": "^22.8.0",
62
63
  "puppeteer-extra": "^3.3.6",
63
64
  "puppeteer-extra-plugin-stealth": "^2.11.2",
64
65
  "queue": "^7.0.0",
65
- "systeminformation": "^5.22.7"
66
+ "systeminformation": "^5.22.8",
67
+ "tar-fs": "^3.0.6"
66
68
  },
67
69
  "optionalDependencies": {
68
- "@types/chai": "^4.3.14",
70
+ "@types/chai": "^4.3.16",
69
71
  "@types/debug": "^4.1.12",
70
72
  "@types/gradient-string": "^1.1.6",
71
73
  "@types/http-proxy": "^1.17.14",
72
- "@types/micromatch": "^4.0.6",
74
+ "@types/micromatch": "^4.0.7",
73
75
  "@types/mocha": "^10.0.6",
74
- "@types/node": "^20.12.7",
76
+ "@types/node": "^20.12.11",
75
77
  "@types/sinon": "^17.0.3",
76
- "@typescript-eslint/eslint-plugin": "^7.6.0",
77
- "@typescript-eslint/parser": "^7.6.0",
78
+ "@typescript-eslint/eslint-plugin": "^7.8.0",
79
+ "@typescript-eslint/parser": "^7.8.0",
78
80
  "assert": "^2.0.0",
79
- "chai": "^5.1.0",
81
+ "chai": "^5.1.1",
80
82
  "cross-env": "^7.0.3",
81
83
  "env-cmd": "^10.1.0",
82
- "esbuild": "^0.20.2",
84
+ "esbuild": "^0.21.2",
83
85
  "esbuild-plugin-polyfill-node": "^0.3.0",
84
86
  "eslint": "^8.57.0",
85
87
  "eslint-plugin-typescript-sort-keys": "^3.2.0",
86
88
  "extract-zip": "^2.0.1",
87
- "marked": "^12.0.1",
89
+ "gunzip-maybe": "^1.4.2",
90
+ "marked": "^12.0.2",
88
91
  "mocha": "^10.4.0",
89
92
  "move-file": "^3.1.0",
90
93
  "prettier": "^3.2.5",
91
- "sinon": "^17.0.1",
94
+ "sinon": "^17.0.2",
92
95
  "ts-node": "^10.9.2",
93
96
  "typescript": "^5.4.5",
94
97
  "typescript-json-schema": "^0.63.0"
@@ -1,20 +1,60 @@
1
- import { cp } from 'fs/promises';
2
- import { fileURLToPath } from 'url';
3
- import path from 'path';
1
+ /* global fetch, console, process */
2
+ import { Readable } from 'stream';
3
+ import { existsSync } from 'fs';
4
+ import { join } from 'path';
5
+ import os from 'os';
4
6
 
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ import { deleteAsync } from 'del';
8
+ import gunzip from 'gunzip-maybe';
9
+ import { moveFile } from 'move-file';
10
+ import tar from 'tar-fs';
6
11
 
7
- const from = path.join(
8
- __dirname,
9
- '..',
10
- 'node_modules',
11
- 'browserless-debugger',
12
- 'static',
13
- );
14
- const to = path.join(__dirname, '..', 'static', 'debugger');
12
+ const registryURL = 'https://registry.npmjs.org/@browserless.io/debugger/';
13
+ const tmp = join(os.tmpdir(), 'browserless-debugger');
14
+ const untarDir = join(tmp, 'package', 'static');
15
+ const debuggerDir = join(process.cwd(), 'static', 'debugger');
16
+
17
+ const lastFromArr = (arr) => arr[arr.length - 1];
18
+ const dlAndExtract = (url) =>
19
+ fetch(url).then(
20
+ (response) =>
21
+ new Promise((resolve, reject) => {
22
+ // @ts-ignore
23
+ Readable.fromWeb(response.body)
24
+ .pipe(gunzip())
25
+ .pipe(tar.extract(tmp))
26
+ .on('error', reject)
27
+ .on('finish', resolve);
28
+ }),
29
+ );
30
+
31
+ const getLatestVersion = async () => {
32
+ const response = await fetch(registryURL);
33
+ const json = await response.json();
34
+ const latest = lastFromArr(Object.keys(json.versions));
35
+ return json.versions[latest];
36
+ };
15
37
 
16
38
  (async () => {
17
- await cp(from, to, {
18
- recursive: true,
39
+ if (existsSync(debuggerDir)) {
40
+ await deleteAsync(debuggerDir);
41
+ }
42
+
43
+ const dist = await getLatestVersion()
44
+ .then((version) => version.dist.tarball)
45
+ .catch((error) => {
46
+ console.error(`Couldn't fetch latest debugger version: ${error.message}`);
47
+ process.exit(1);
48
+ });
49
+
50
+ await dlAndExtract(dist).catch((error) => {
51
+ console.error(`Couldn't download debugger: ${error.message}`);
52
+ process.exit(1);
19
53
  });
20
- })();
54
+
55
+ await moveFile(untarDir, debuggerDir);
56
+ await deleteAsync(tmp, { force: true });
57
+ })().catch((error) => {
58
+ console.error(`An error occurred: ${error.message}`);
59
+ process.exit(1);
60
+ });