@browserless.io/browserless 2.0.0-beta-8 → 2.1.1

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 (71) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/README.md +10 -6
  3. package/bin/browserless.js +49 -13
  4. package/bin/scaffold/README.md +7 -7
  5. package/bin/scaffold/tsconfig.json +1 -1
  6. package/build/browserless.js +9 -2
  7. package/build/browsers/cdp-chromium.d.ts +1 -1
  8. package/build/browsers/cdp-chromium.js +4 -5
  9. package/build/browsers/index.d.ts +10 -0
  10. package/build/browsers/index.js +44 -0
  11. package/build/browsers/playwright-chromium.d.ts +1 -1
  12. package/build/browsers/playwright-chromium.js +1 -2
  13. package/build/browsers/playwright-firefox.d.ts +1 -1
  14. package/build/browsers/playwright-firefox.js +1 -2
  15. package/build/browsers/playwright-webkit.d.ts +1 -1
  16. package/build/browsers/playwright-webkit.js +1 -2
  17. package/build/config.d.ts +9 -0
  18. package/build/config.js +15 -0
  19. package/build/constants.d.ts +1 -0
  20. package/build/constants.js +1 -0
  21. package/build/data/classes.json +1 -1
  22. package/build/data/selectors.json +1 -1
  23. package/build/file-system.spec.js +1 -1
  24. package/build/http.d.ts +4 -0
  25. package/build/http.js +4 -0
  26. package/build/routes/chromium/http/content-post.body.json +8 -8
  27. package/build/routes/chromium/http/json-list.d.ts +15 -0
  28. package/build/routes/chromium/http/json-list.js +23 -0
  29. package/build/routes/chromium/http/json-list.response.json +52 -0
  30. package/build/routes/chromium/http/json-new.d.ts +15 -0
  31. package/build/routes/chromium/http/json-new.js +23 -0
  32. package/build/routes/chromium/http/json-new.response.json +44 -0
  33. package/build/routes/chromium/http/json-protocol-get.d.ts +15 -0
  34. package/build/routes/chromium/http/json-protocol-get.js +20 -0
  35. package/build/routes/chromium/http/json-protocol-get.response.json +6 -0
  36. package/build/routes/chromium/http/json-version-get.d.ts +15 -0
  37. package/build/routes/chromium/http/json-version-get.js +30 -0
  38. package/build/routes/chromium/http/json-version-get.response.json +37 -0
  39. package/build/routes/chromium/http/pdf-post.body.json +12 -8
  40. package/build/routes/chromium/http/scrape-post.body.json +8 -8
  41. package/build/routes/chromium/http/screenshot-post.body.json +8 -8
  42. package/build/routes/chromium/tests/json-version.spec.d.ts +1 -0
  43. package/build/routes/chromium/tests/json-version.spec.js +37 -0
  44. package/build/routes/chromium/utils/cdp.d.ts +2 -0
  45. package/build/routes/chromium/utils/cdp.js +14 -0
  46. package/build/types.d.ts +31 -0
  47. package/build/utils.d.ts +9 -0
  48. package/build/utils.js +17 -2
  49. package/package.json +15 -18
  50. package/src/browserless.ts +12 -1
  51. package/src/browsers/cdp-chromium.ts +7 -10
  52. package/src/browsers/index.ts +65 -2
  53. package/src/browsers/playwright-chromium.ts +3 -4
  54. package/src/browsers/playwright-firefox.ts +3 -4
  55. package/src/browsers/playwright-webkit.ts +3 -4
  56. package/src/config.ts +17 -0
  57. package/src/constants.ts +1 -0
  58. package/src/file-system.spec.ts +1 -1
  59. package/src/http.ts +4 -0
  60. package/src/routes/chromium/http/json-list.ts +50 -0
  61. package/src/routes/chromium/http/json-new.ts +50 -0
  62. package/src/routes/chromium/http/json-protocol-get.ts +38 -0
  63. package/src/routes/chromium/http/json-version-get.ts +55 -0
  64. package/src/routes/chromium/tests/json-version.spec.ts +52 -0
  65. package/src/routes/chromium/utils/cdp.ts +19 -0
  66. package/src/types.ts +38 -0
  67. package/src/utils.ts +26 -4
  68. package/static/docs/swagger.json +404 -10
  69. package/static/function/client.js +2328 -1975
  70. package/browser.json +0 -7
  71. package/scripts/install-cdp-json.js +0 -37
package/build/types.d.ts CHANGED
@@ -438,4 +438,35 @@ export interface IBrowserlessStats {
438
438
  unhealthy: number;
439
439
  units: number;
440
440
  }
441
+ export interface CDPJSONPayload {
442
+ /**
443
+ * The description of the target. Generally the page's title.
444
+ */
445
+ description: string;
446
+ /**
447
+ * The fully-qualified URL of the Devtools inspector app.
448
+ */
449
+ devtoolsFrontendUrl: string;
450
+ /**
451
+ * A Unique Id for the underlying target.
452
+ */
453
+ id: string;
454
+ /**
455
+ * The title of the target. For pages this is the page's title.
456
+ */
457
+ title: string;
458
+ /**
459
+ * The type of target, generally "page" or "background_page".
460
+ */
461
+ type: string;
462
+ /**
463
+ * The current URL the target is consuming or visiting.
464
+ */
465
+ url: string;
466
+ /**
467
+ * The target or page's WebSocket Debugger URL. Primarily used for legacy
468
+ * libraries to connect and inspect or remote automate this target.
469
+ */
470
+ webSocketDebuggerUrl: string;
471
+ }
441
472
  export {};
package/build/utils.d.ts CHANGED
@@ -12,6 +12,15 @@ export declare const tsExtension = ".d.ts";
12
12
  export declare const jsonExtension = ".json";
13
13
  export declare const jsExtension = ".js";
14
14
  export declare const id: () => string;
15
+ /**
16
+ * Generates a random, Chrome-compliant page ID with "BLESS"
17
+ * prepended. This prepended text signals to other parts of the
18
+ * system that this is a Browserless-created ID so it can be appropriately
19
+ * handled.
20
+ *
21
+ * @returns {string} A random Page ID
22
+ */
23
+ export declare const pageID: () => string;
15
24
  export declare const createLogger: (domain: string) => debug.Debugger;
16
25
  export declare const dedent: (strings: string | string[], ...values: string[]) => string;
17
26
  export declare const isConnected: (connection: Duplex | ServerResponse) => boolean;
package/build/utils.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as fs from 'fs/promises';
2
- import { CDPChromium, PlaywrightChromium, PlaywrightFirefox, PlaywrightWebkit, codes, contentTypes, encodings, encryptionAlgo, encryptionSep, } from '@browserless.io/browserless';
2
+ import { BLESS_PAGE_IDENTIFIER, CDPChromium, PlaywrightChromium, PlaywrightFirefox, PlaywrightWebkit, codes, contentTypes, encodings, encryptionAlgo, encryptionSep, } from '@browserless.io/browserless';
3
3
  import playwright from 'playwright-core';
4
4
  import crypto from 'crypto';
5
5
  import debug from 'debug';
@@ -26,6 +26,21 @@ export const tsExtension = '.d.ts';
26
26
  export const jsonExtension = '.json';
27
27
  export const jsExtension = '.js';
28
28
  export const id = () => crypto.randomUUID();
29
+ /**
30
+ * Generates a random, Chrome-compliant page ID with "BLESS"
31
+ * prepended. This prepended text signals to other parts of the
32
+ * system that this is a Browserless-created ID so it can be appropriately
33
+ * handled.
34
+ *
35
+ * @returns {string} A random Page ID
36
+ */
37
+ export const pageID = () => {
38
+ const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
39
+ const id = Array.from({ length: 32 - BLESS_PAGE_IDENTIFIER.length })
40
+ .map(() => chars[Math.floor(Math.random() * chars.length)])
41
+ .join('');
42
+ return `${BLESS_PAGE_IDENTIFIER}${id}`;
43
+ };
29
44
  export const createLogger = (domain) => {
30
45
  return debug(`browserless.io:${domain}`);
31
46
  };
@@ -158,7 +173,7 @@ export const removeNullStringify = (json, allowNull = true) => {
158
173
  return value;
159
174
  if (value !== null)
160
175
  return value;
161
- });
176
+ }, ' ');
162
177
  };
163
178
  export const jsonOrString = (maybeJson) => safeParse(maybeJson) ?? maybeJson;
164
179
  export const readBody = async (req) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserless.io/browserless",
3
- "version": "2.0.0-beta-8",
3
+ "version": "2.1.1",
4
4
  "license": "SSPL",
5
5
  "description": "The browserless platform",
6
6
  "author": "browserless.io",
@@ -25,8 +25,7 @@
25
25
  "dev": "npm run build:dev && env-cmd -f .env node build",
26
26
  "install:adblock": "node scripts/install-adblock.js",
27
27
  "install:browsers": "npx --yes playwright install chromium firefox webkit",
28
- "install:cdp-json": "node scripts/install-cdp-json.js",
29
- "install:dev": "npm run install:browsers && npm run install:cdp-json",
28
+ "install:dev": "npm run install:browsers",
30
29
  "lint": "eslint . --ext .ts --fix",
31
30
  "prepack": "npm run build:dev",
32
31
  "prettier": "prettier '{src,functions,scripts,bin,external,bin}/**/*.{js,ts,json}' --log-level error --write",
@@ -43,7 +42,6 @@
43
42
  "scripts/*",
44
43
  "src/*",
45
44
  "static/*",
46
- "browser.json",
47
45
  "CHANGELOG.md",
48
46
  "tsconfig.json"
49
47
  ],
@@ -51,18 +49,18 @@
51
49
  "debug": "^4.3.2",
52
50
  "del": "^7.0.0",
53
51
  "enjoi": "^9.0.1",
54
- "file-type": "^18.7.0",
52
+ "file-type": "^19.0.0",
55
53
  "get-port": "^7.0.0",
56
54
  "gradient-string": "^2.0.0",
57
55
  "http-proxy": "^1.18.1",
58
56
  "lighthouse": "^11.1.0",
59
57
  "micromatch": "^4.0.4",
60
- "playwright-core": "^1.40.1",
61
- "puppeteer-core": "^21.6.1",
58
+ "playwright-core": "^1.41.2",
59
+ "puppeteer-core": "^21.10.0",
62
60
  "puppeteer-extra": "^3.3.6",
63
61
  "puppeteer-extra-plugin-stealth": "^2.11.2",
64
62
  "queue": "^7.0.0",
65
- "systeminformation": "^5.21.20"
63
+ "systeminformation": "^5.21.24"
66
64
  },
67
65
  "optionalDependencies": {
68
66
  "@types/chai": "^4.3.11",
@@ -71,25 +69,24 @@
71
69
  "@types/http-proxy": "^1.17.14",
72
70
  "@types/micromatch": "^4.0.6",
73
71
  "@types/mocha": "^10.0.6",
74
- "@types/node": "^20.10.8",
75
- "@types/sinon": "^17.0.2",
76
- "@typescript-eslint/eslint-plugin": "^6.18.1",
77
- "@typescript-eslint/parser": "^6.18.1",
72
+ "@types/node": "^20.11.16",
73
+ "@types/sinon": "^17.0.3",
74
+ "@typescript-eslint/eslint-plugin": "^6.20.0",
75
+ "@typescript-eslint/parser": "^6.21.0",
78
76
  "assert": "^2.0.0",
79
- "chai": "^5.0.0",
77
+ "chai": "^5.0.3",
80
78
  "cross-env": "^7.0.3",
81
79
  "env-cmd": "^10.1.0",
82
- "esbuild": "^0.19.11",
80
+ "esbuild": "^0.20.0",
83
81
  "esbuild-plugin-polyfill-node": "^0.3.0",
84
82
  "eslint": "^8.56.0",
85
83
  "eslint-plugin-import": "^2.29.1",
86
84
  "eslint-plugin-typescript-sort-keys": "^3.1.0",
87
85
  "extract-zip": "^2.0.1",
88
- "marked": "^11.1.0",
86
+ "marked": "^12.0.0",
89
87
  "mocha": "^10.0.0",
90
88
  "move-file": "^3.1.0",
91
- "nodemon": "^3.0.2",
92
- "prettier": "^3.1.1",
89
+ "prettier": "^3.2.5",
93
90
  "sinon": "^17.0.1",
94
91
  "ts-node": "^10.9.2",
95
92
  "typescript": "^5.3.3",
@@ -144,7 +141,7 @@
144
141
  "multiple",
145
142
  "single"
146
143
  ],
147
- "allowSeparatedGroups": false
144
+ "allowSeparatedGroups": true
148
145
  }
149
146
  ]
150
147
  }
@@ -3,6 +3,7 @@ import {
3
3
  BrowserHTTPRoute,
4
4
  BrowserManager,
5
5
  BrowserWebsocketRoute,
6
+ CDPChromium,
6
7
  Config,
7
8
  FileSystem,
8
9
  HTTPRoute,
@@ -11,6 +12,9 @@ import {
11
12
  Limiter,
12
13
  Metrics,
13
14
  Monitoring,
15
+ PlaywrightChromium,
16
+ PlaywrightFirefox,
17
+ PlaywrightWebkit,
14
18
  Router,
15
19
  Token,
16
20
  WebHooks,
@@ -158,6 +162,12 @@ export class Browserless {
158
162
  public async start() {
159
163
  const httpRoutes: Array<HTTPRoute | BrowserHTTPRoute> = [];
160
164
  const wsRoutes: Array<WebSocketRoute | BrowserWebsocketRoute> = [];
165
+ const internalBrowsers = [
166
+ CDPChromium,
167
+ PlaywrightFirefox,
168
+ PlaywrightChromium,
169
+ PlaywrightWebkit,
170
+ ];
161
171
 
162
172
  const [[httpRouteFiles, wsRouteFiles], installedBrowsers] =
163
173
  await Promise.all([getRouteFiles(this.config), availableBrowsers]);
@@ -254,11 +264,12 @@ export class Browserless {
254
264
  }
255
265
  }
256
266
 
257
- // Validate that browsers are installed and route paths are unique
267
+ // Validate that we have the browsers they are asking for
258
268
  [...httpRoutes, ...wsRoutes].forEach((route) => {
259
269
  if (
260
270
  'browser' in route &&
261
271
  route.browser &&
272
+ internalBrowsers.includes(route.browser) &&
262
273
  !installedBrowsers.some((b) => b.name === route.browser?.name)
263
274
  ) {
264
275
  throw new Error(
@@ -314,17 +314,16 @@ export class CDPChromium extends EventEmitter {
314
314
 
315
315
  public wsEndpoint = (): string | null => this.browserWSEndpoint;
316
316
 
317
- public publicWSEndpoint = (token: string): string | null => {
317
+ public publicWSEndpoint = (token: string | null): string | null => {
318
318
  if (!this.browserWSEndpoint) {
319
319
  return null;
320
320
  }
321
321
 
322
+ const serverURL = new URL(this.config.getExternalWebSocketAddress());
322
323
  const wsURL = new URL(this.browserWSEndpoint);
323
- const serverURL = new URL(this.config.getExternalAddress());
324
-
325
324
  wsURL.hostname = serverURL.hostname;
326
325
  wsURL.port = serverURL.port;
327
- wsURL.protocol = serverURL.protocol === 'https' ? 'wss' : 'ws';
326
+
328
327
  if (token) {
329
328
  wsURL.searchParams.set('token', token);
330
329
  }
@@ -345,16 +344,14 @@ export class CDPChromium extends EventEmitter {
345
344
  }
346
345
  socket.once('close', resolve);
347
346
 
348
- this.debug(
349
- `Proxying ${req.parsed.href} to browser ${this.browserWSEndpoint}`,
350
- );
351
-
352
347
  const [page] = await this.browser.pages();
353
348
  const pageLocation = `/devtools/page/${this.getPageId(page)}`;
354
349
 
355
350
  this.debug(`Proxying ${req.parsed.href} to page "${pageLocation}"`);
356
351
 
357
- req.url = pageLocation;
352
+ const target = new URL(pageLocation, this.browserWSEndpoint).href;
353
+
354
+ req.url = '';
358
355
 
359
356
  this.proxy.ws(
360
357
  req,
@@ -362,7 +359,7 @@ export class CDPChromium extends EventEmitter {
362
359
  head,
363
360
  {
364
361
  changeOrigin: true,
365
- target: this.browserWSEndpoint,
362
+ target,
366
363
  },
367
364
  (error) => {
368
365
  this.debug(`Error proxying session: ${error}`);
@@ -82,6 +82,69 @@ export class BrowserManager {
82
82
  return dataDirPath;
83
83
  };
84
84
 
85
+ public getProtocolJSON = async (): Promise<object> => {
86
+ this.debug(`Launching Chrome to generate /json/protocol results`);
87
+ const browser = new CDPChromium({
88
+ blockAds: false,
89
+ config: this.config,
90
+ record: false,
91
+ userDataDir: null,
92
+ });
93
+ await browser.launch();
94
+ const wsEndpoint = browser.wsEndpoint();
95
+
96
+ if (!wsEndpoint) {
97
+ throw new Error('There was an error launching the browser');
98
+ }
99
+
100
+ const { port } = new URL(wsEndpoint);
101
+ const res = await fetch(`http://127.0.0.1:${port}/json/protocol`);
102
+ const protocolJSON = await res.json();
103
+
104
+ browser.close();
105
+
106
+ return protocolJSON;
107
+ };
108
+
109
+ public getVersionJSON = async (): Promise<{
110
+ Browser: string;
111
+ 'Debugger-Version': string;
112
+ 'Protocol-Version': string;
113
+ 'User-Agent': string;
114
+ 'V8-Version': string;
115
+ 'WebKit-Version': string;
116
+ webSocketDebuggerUrl: string;
117
+ }> => {
118
+ this.debug(`Launching Chrome to generate /json/version results`);
119
+ const browser = new CDPChromium({
120
+ blockAds: false,
121
+ config: this.config,
122
+ record: false,
123
+ userDataDir: null,
124
+ });
125
+ await browser.launch();
126
+ const wsEndpoint = browser.wsEndpoint();
127
+
128
+ if (!wsEndpoint) {
129
+ throw new Error('There was an error launching the browser');
130
+ }
131
+
132
+ const { port } = new URL(wsEndpoint);
133
+ const res = await fetch(`http://127.0.0.1:${port}/json/version`);
134
+ const meta = await res.json();
135
+
136
+ browser.close();
137
+
138
+ const { 'WebKit-Version': webkitVersion } = meta;
139
+ const debuggerVersion = webkitVersion.match(/\s\(@(\b[0-9a-f]{5,40}\b)/)[1];
140
+
141
+ return {
142
+ ...meta,
143
+ 'Debugger-Version': debuggerVersion,
144
+ webSocketDebuggerUrl: this.config.getExternalWebSocketAddress(),
145
+ };
146
+ };
147
+
85
148
  private generateSessionJson = async (
86
149
  browser: BrowserInstance,
87
150
  session: BrowserlessSession,
@@ -201,8 +264,8 @@ export class BrowserManager {
201
264
  if (req.parsed.pathname.includes('/devtools/browser')) {
202
265
  const sessions = Array.from(this.browsers);
203
266
  const id = req.parsed.pathname.split('/').pop() as string;
204
- const browser = sessions.find(
205
- ([b]) => b.wsEndpoint()?.includes(req.parsed.pathname),
267
+ const browser = sessions.find(([b]) =>
268
+ b.wsEndpoint()?.includes(req.parsed.pathname),
206
269
  );
207
270
 
208
271
  if (browser) {
@@ -106,17 +106,16 @@ export class PlaywrightChromium extends EventEmitter {
106
106
 
107
107
  public wsEndpoint = (): string | null => this.browserWSEndpoint;
108
108
 
109
- public publicWSEndpoint = (token: string): string | null => {
109
+ public publicWSEndpoint = (token: string | null): string | null => {
110
110
  if (!this.browserWSEndpoint) {
111
111
  return null;
112
112
  }
113
113
 
114
+ const serverURL = new URL(this.config.getExternalWebSocketAddress());
114
115
  const wsURL = new URL(this.browserWSEndpoint);
115
- const serverURL = new URL(this.config.getExternalAddress());
116
-
117
116
  wsURL.hostname = serverURL.hostname;
118
117
  wsURL.port = serverURL.port;
119
- wsURL.protocol = serverURL.protocol === 'https' ? 'wss' : 'ws';
118
+
120
119
  if (token) {
121
120
  wsURL.searchParams.set('token', token);
122
121
  }
@@ -99,17 +99,16 @@ export class PlaywrightFirefox extends EventEmitter {
99
99
 
100
100
  public wsEndpoint = (): string | null => this.browserWSEndpoint;
101
101
 
102
- public publicWSEndpoint = (token: string): string | null => {
102
+ public publicWSEndpoint = (token: string | null): string | null => {
103
103
  if (!this.browserWSEndpoint) {
104
104
  return null;
105
105
  }
106
106
 
107
+ const serverURL = new URL(this.config.getExternalWebSocketAddress());
107
108
  const wsURL = new URL(this.browserWSEndpoint);
108
- const serverURL = new URL(this.config.getExternalAddress());
109
-
110
109
  wsURL.hostname = serverURL.hostname;
111
110
  wsURL.port = serverURL.port;
112
- wsURL.protocol = serverURL.protocol === 'https' ? 'wss' : 'ws';
111
+
113
112
  if (token) {
114
113
  wsURL.searchParams.set('token', token);
115
114
  }
@@ -99,17 +99,16 @@ export class PlaywrightWebkit extends EventEmitter {
99
99
 
100
100
  public wsEndpoint = (): string | null => this.browserWSEndpoint;
101
101
 
102
- public publicWSEndpoint = (token: string): string | null => {
102
+ public publicWSEndpoint = (token: string | null): string | null => {
103
103
  if (!this.browserWSEndpoint) {
104
104
  return null;
105
105
  }
106
106
 
107
+ const serverURL = new URL(this.config.getExternalWebSocketAddress());
107
108
  const wsURL = new URL(this.browserWSEndpoint);
108
- const serverURL = new URL(this.config.getExternalAddress());
109
-
110
109
  wsURL.hostname = serverURL.hostname;
111
110
  wsURL.port = serverURL.port;
112
- wsURL.protocol = serverURL.protocol === 'https' ? 'wss' : 'ws';
111
+
113
112
  if (token) {
114
113
  wsURL.searchParams.set('token', token);
115
114
  }
package/src/config.ts CHANGED
@@ -409,6 +409,23 @@ export class Config extends EventEmitter {
409
409
  public getExternalAddress = (): string =>
410
410
  this.external ?? this.getServerAddress();
411
411
 
412
+ /**
413
+ * Returns the the fully-qualified WebSocket URL for the
414
+ * external address that browserless might be
415
+ * running behind *or* the server address if
416
+ * no external URL is provided.
417
+ *
418
+ * @returns {string} The URL to reach the server
419
+ */
420
+ public getExternalWebSocketAddress = (): string => {
421
+ const httpAddress = new URL(this.external ?? this.getServerAddress());
422
+ httpAddress.protocol = httpAddress.protocol.startsWith('https')
423
+ ? 'wss:'
424
+ : 'ws:';
425
+
426
+ return httpAddress.href;
427
+ };
428
+
412
429
  /**
413
430
  * When CORS is enabled, returns relevant CORS headers
414
431
  * to requests and for the OPTIONS call. Values can be
package/src/constants.ts CHANGED
@@ -2,3 +2,4 @@ export const encryptionAlgo = 'aes-192-cbc';
2
2
  export const encryptionSep = '.';
3
3
  export const liveURLSep = ':';
4
4
  export const keyLength = 24;
5
+ export const BLESS_PAGE_IDENTIFIER = 'BLESS';
@@ -4,7 +4,7 @@ import { expect } from 'chai';
4
4
 
5
5
  const filePath = '/tmp/_browserless_test_fs_';
6
6
 
7
- describe.only('File-System', () => {
7
+ describe('File-System', () => {
8
8
  afterEach(async () => unlink(filePath));
9
9
 
10
10
  it('saves and encodes files', async () => {
package/src/http.ts CHANGED
@@ -92,6 +92,10 @@ export enum HTTPRoutes {
92
92
  content = '/content',
93
93
  download = '/download',
94
94
  function = '/function',
95
+ jsonList = '/json/list',
96
+ jsonNew = '/json/new',
97
+ jsonProtocol = '/json/protocol',
98
+ jsonVersion = '/json/version',
95
99
  pdf = '/pdf',
96
100
  performance = '/performance',
97
101
  scrape = '/scrape',
@@ -0,0 +1,50 @@
1
+ import {
2
+ APITags,
3
+ HTTPRoute,
4
+ HTTPRoutes,
5
+ Methods,
6
+ Request,
7
+ Response,
8
+ contentTypes,
9
+ dedent,
10
+ jsonResponse,
11
+ } from '@browserless.io/browserless';
12
+ import { getCDPJSONPayload } from '../utils/cdp.js';
13
+
14
+ /*
15
+ Example Payload from Chrome:
16
+ [{
17
+ "description": "",
18
+ "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/6CA38A3E207BA534C674D1057B19E9CC",
19
+ "id": "6CA38A3E207BA534C674D1057B19E9CC",
20
+ "title": "New Tab",
21
+ "type": "page",
22
+ "url": "http://localhost:9222/json/list",
23
+ "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/6CA38A3E207BA534C674D1057B19E9CC"
24
+ }]
25
+ */
26
+ export type ResponseSchema = Array<ReturnType<typeof getCDPJSONPayload>>;
27
+
28
+ export default class GetJSONList extends HTTPRoute {
29
+ accepts = [contentTypes.any];
30
+ auth = true;
31
+ browser = null;
32
+ concurrency = false;
33
+ contentTypes = [contentTypes.json];
34
+ description = dedent(`
35
+ Returns a JSON payload that acts as a pass-through to the DevTools /json/list HTTP API in Chromium.
36
+ Browserless mocks this payload so that remote clients can connect to the underlying "webSocketDebuggerUrl"
37
+ which will cause Browserless to start the browser and proxy that request into a blank page.
38
+ `);
39
+ method = Methods.get;
40
+ path = HTTPRoutes.jsonList;
41
+ tags = [APITags.browserAPI];
42
+
43
+ handler = async (_req: Request, res: Response): Promise<void> => {
44
+ const config = this.config();
45
+ const externalAddress = config.getExternalAddress();
46
+ const payload = getCDPJSONPayload(externalAddress);
47
+
48
+ return jsonResponse(res, 200, [payload] as ResponseSchema);
49
+ };
50
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ APITags,
3
+ HTTPRoute,
4
+ HTTPRoutes,
5
+ Methods,
6
+ Request,
7
+ Response,
8
+ contentTypes,
9
+ dedent,
10
+ jsonResponse,
11
+ } from '@browserless.io/browserless';
12
+ import { getCDPJSONPayload } from '../utils/cdp.js';
13
+
14
+ /*
15
+ Example Payload from Chrome:
16
+ {
17
+ "description": "",
18
+ "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/2F76525C32A916DF30C4F37A4970B8BF",
19
+ "id": "2F76525C32A916DF30C4F37A4970B8BF",
20
+ "title": "",
21
+ "type": "page",
22
+ "url": "about:blank",
23
+ "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/2F76525C32A916DF30C4F37A4970B8BF"
24
+ }
25
+ */
26
+ export type ResponseSchema = ReturnType<typeof getCDPJSONPayload>;
27
+
28
+ export default class GetJSONList extends HTTPRoute {
29
+ accepts = [contentTypes.any];
30
+ auth = true;
31
+ browser = null;
32
+ concurrency = false;
33
+ contentTypes = [contentTypes.json];
34
+ description = dedent(`
35
+ Returns a JSON payload that acts as a pass-through to the DevTools /json/list HTTP API in Chromium.
36
+ Browserless mocks this payload so that remote clients can connect to the underlying "webSocketDebuggerUrl"
37
+ which will cause Browserless to start the browser and proxy that request into a blank page.
38
+ `);
39
+ method = Methods.put;
40
+ path = HTTPRoutes.jsonNew;
41
+ tags = [APITags.browserAPI];
42
+
43
+ handler = async (_req: Request, res: Response): Promise<void> => {
44
+ const config = this.config();
45
+ const externalAddress = config.getExternalAddress();
46
+ const payload = getCDPJSONPayload(externalAddress);
47
+
48
+ return jsonResponse(res, 200, payload);
49
+ };
50
+ }
@@ -0,0 +1,38 @@
1
+ import {
2
+ APITags,
3
+ HTTPRoute,
4
+ HTTPRoutes,
5
+ Methods,
6
+ Request,
7
+ Response,
8
+ contentTypes,
9
+ jsonResponse,
10
+ } from '@browserless.io/browserless';
11
+
12
+ // @TODO Figure out how to parse the Protocol JSON into a TS definition
13
+ // for our openapi docs.
14
+ export type ResponseSchema = object;
15
+
16
+ export default class GetJSONVersion extends HTTPRoute {
17
+ accepts = [contentTypes.any];
18
+ auth = true;
19
+ browser = null;
20
+ concurrency = false;
21
+ contentTypes = [contentTypes.json];
22
+ description = `Returns Protocol JSON meta-data that Chrome comes with.`;
23
+ method = Methods.get;
24
+ path = HTTPRoutes.jsonProtocol;
25
+ tags = [APITags.browserAPI];
26
+
27
+ private cachedProtocol: object | undefined;
28
+
29
+ handler = async (_req: Request, res: Response): Promise<void> => {
30
+ const browserManager = this.browserManager();
31
+
32
+ if (!this.cachedProtocol) {
33
+ this.cachedProtocol = await browserManager.getProtocolJSON();
34
+ }
35
+
36
+ return jsonResponse(res, 200, this.cachedProtocol);
37
+ };
38
+ }
@@ -0,0 +1,55 @@
1
+ import {
2
+ APITags,
3
+ BrowserManager,
4
+ HTTPRoute,
5
+ HTTPRoutes,
6
+ Methods,
7
+ Request,
8
+ Response,
9
+ UnwrapPromise,
10
+ contentTypes,
11
+ jsonResponse,
12
+ writeResponse,
13
+ } from '@browserless.io/browserless';
14
+
15
+ export type ResponseSchema = UnwrapPromise<
16
+ ReturnType<BrowserManager['getVersionJSON']>
17
+ >;
18
+
19
+ export default class GetJSONVersion extends HTTPRoute {
20
+ accepts = [contentTypes.any];
21
+ auth = true;
22
+ browser = null;
23
+ concurrency = false;
24
+ contentTypes = [contentTypes.json];
25
+ description = `Returns a JSON payload that acts as a pass-through to the DevTools /json/version protocol in Chrome.`;
26
+ method = Methods.get;
27
+ path = HTTPRoutes.jsonVersion;
28
+ tags = [APITags.browserAPI];
29
+
30
+ private cachedJSON: ResponseSchema | undefined;
31
+
32
+ handler = async (req: Request, res: Response): Promise<void> => {
33
+ const baseUrl = req.parsed.host;
34
+ const protocol = req.parsed.protocol.includes('s') ? 'wss' : 'ws';
35
+ const browserManager = this.browserManager();
36
+
37
+ try {
38
+ if (!this.cachedJSON) {
39
+ this.cachedJSON = {
40
+ ...(await browserManager.getVersionJSON()),
41
+ webSocketDebuggerUrl: `${protocol}://${baseUrl}`,
42
+ };
43
+ }
44
+
45
+ return jsonResponse(res, 200, this.cachedJSON);
46
+ } catch (err) {
47
+ return writeResponse(
48
+ res,
49
+ 500,
50
+ 'There was an error handling your request',
51
+ contentTypes.text,
52
+ );
53
+ }
54
+ };
55
+ }