@browserless.io/browserless 2.3.0 → 2.4.0-beta-3

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 (100) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/bin/browserless.js +2 -0
  3. package/bin/scaffold/README.md +26 -1
  4. package/build/browserless.d.ts +6 -2
  5. package/build/browserless.js +23 -4
  6. package/build/browsers/chromium.cdp.d.ts +1 -5
  7. package/build/browsers/chromium.cdp.js +5 -115
  8. package/build/browsers/chromium.playwright.d.ts +1 -3
  9. package/build/browsers/chromium.playwright.js +1 -6
  10. package/build/browsers/firefox.playwright.d.ts +1 -3
  11. package/build/browsers/firefox.playwright.js +1 -6
  12. package/build/browsers/index.d.ts +5 -1
  13. package/build/browsers/index.js +6 -5
  14. package/build/browsers/webkit.playwright.d.ts +1 -3
  15. package/build/browsers/webkit.playwright.js +1 -6
  16. package/build/config.d.ts +9 -0
  17. package/build/config.js +11 -0
  18. package/build/constants.d.ts +0 -1
  19. package/build/constants.js +0 -1
  20. package/build/data/selectors.json +1 -1
  21. package/build/file-system.d.ts +12 -1
  22. package/build/file-system.js +14 -1
  23. package/build/http.d.ts +0 -7
  24. package/build/limiter.d.ts +9 -0
  25. package/build/limiter.js +11 -0
  26. package/build/metrics.d.ts +12 -1
  27. package/build/metrics.js +13 -1
  28. package/build/monitoring.d.ts +12 -1
  29. package/build/monitoring.js +14 -1
  30. package/build/router.d.ts +12 -2
  31. package/build/router.js +16 -6
  32. package/build/routes/chrome/http/content.post.body.json +8 -8
  33. package/build/routes/chrome/http/content.post.query.json +0 -4
  34. package/build/routes/chrome/http/download.post.query.json +0 -4
  35. package/build/routes/chrome/http/function.post.query.json +0 -4
  36. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  37. package/build/routes/chrome/http/pdf.post.query.json +0 -4
  38. package/build/routes/chrome/http/performance.post.query.json +0 -4
  39. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  40. package/build/routes/chrome/http/scrape.post.query.json +0 -4
  41. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  42. package/build/routes/chrome/http/screenshot.post.query.json +0 -4
  43. package/build/routes/chrome/tests/function.spec.js +32 -0
  44. package/build/routes/chrome/ws/browser.query.json +0 -4
  45. package/build/routes/chrome/ws/cdp.query.json +0 -4
  46. package/build/routes/chrome/ws/page.query.json +0 -4
  47. package/build/routes/chrome/ws/playwright.query.json +0 -4
  48. package/build/routes/chromium/http/content.post.body.json +8 -8
  49. package/build/routes/chromium/http/content.post.query.json +0 -4
  50. package/build/routes/chromium/http/download.post.query.json +0 -4
  51. package/build/routes/chromium/http/function.post.query.json +0 -4
  52. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  53. package/build/routes/chromium/http/pdf.post.query.json +0 -4
  54. package/build/routes/chromium/http/performance.post.query.json +0 -4
  55. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  56. package/build/routes/chromium/http/scrape.post.query.json +0 -4
  57. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  58. package/build/routes/chromium/http/screenshot.post.query.json +0 -4
  59. package/build/routes/chromium/ws/browser.query.json +0 -4
  60. package/build/routes/chromium/ws/cdp.query.json +0 -4
  61. package/build/routes/chromium/ws/page.query.json +0 -4
  62. package/build/routes/chromium/ws/playwright.query.json +0 -4
  63. package/build/routes/firefox/ws/playwright.query.json +0 -4
  64. package/build/routes/management/http/static.get.js +17 -11
  65. package/build/routes/webkit/ws/playwright.query.json +0 -4
  66. package/build/server.d.ts +8 -3
  67. package/build/server.js +15 -13
  68. package/build/token.d.ts +12 -1
  69. package/build/token.js +14 -1
  70. package/build/types.d.ts +9 -12
  71. package/build/types.js +10 -1
  72. package/build/webhooks.d.ts +12 -1
  73. package/build/webhooks.js +14 -1
  74. package/extensions/.gitkeep +0 -0
  75. package/package.json +5 -5
  76. package/src/browserless.ts +24 -2
  77. package/src/browsers/chromium.cdp.ts +10 -157
  78. package/src/browsers/chromium.playwright.ts +0 -8
  79. package/src/browsers/firefox.playwright.ts +0 -8
  80. package/src/browsers/index.ts +7 -6
  81. package/src/browsers/webkit.playwright.ts +0 -8
  82. package/src/config.ts +13 -0
  83. package/src/constants.ts +0 -1
  84. package/src/file-system.ts +16 -1
  85. package/src/http.ts +0 -8
  86. package/src/limiter.ts +13 -0
  87. package/src/metrics.ts +16 -1
  88. package/src/monitoring.ts +18 -2
  89. package/src/router.ts +20 -9
  90. package/src/routes/chrome/tests/function.spec.ts +35 -0
  91. package/src/routes/management/http/static.get.ts +25 -15
  92. package/src/server.ts +18 -16
  93. package/src/token.ts +18 -2
  94. package/src/types.ts +9 -13
  95. package/src/webhooks.ts +18 -2
  96. package/static/docs/swagger.json +10 -186
  97. package/static/docs/swagger.min.json +9 -185
  98. package/extensions/screencast/background.js +0 -143
  99. package/extensions/screencast/content_script.js +0 -18
  100. package/extensions/screencast/manifest.json +0 -19
package/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
1
  # [Latest](https://github.com/browserless/chrome/compare/v2.3.0...main)
2
+ **Potentially Breaking**
3
+ - Drops support for recording and screencasting in favor of library-based approaches.
4
+
5
+ **Other Changes**
6
+ - Adds support for user-specified `stop` methods in SDK Module extensions.
7
+ - Core modules now extend `EventEmitter`, making them able to publish events.
2
8
  - Dependency updates.
3
9
 
4
10
  # [v2.3.0](https://github.com/browserless/chrome/compare/v2.2.0...v2.3.0)
@@ -319,6 +319,8 @@ const start = async (dev = false) => {
319
319
  webhooks,
320
320
  });
321
321
 
322
+ browserless.setStaticSDKDir(path.join(projectDir, 'static'));
323
+
322
324
  if (disabledRoutes !== undefined) {
323
325
  if (!Array.isArray(disabledRoutes)) {
324
326
  throw new Error(
@@ -13,7 +13,8 @@ Finally, this SDK and Browserless.io are built to support businesses and enterpr
13
13
  - [Routing](#routing)
14
14
  - [Utilities](#utilities)
15
15
  - [Extending Modules](#extending-modules)
16
- - [Disabling Routes](#dis)
16
+ - [Disabling Routes](#disabling-routes)
17
+ - [Serving Static Files](#serving-static-files)
17
18
  - [Running in Development](#running-in-development)
18
19
  - [Building for Production](#building-for-production)
19
20
  - [Running without Building](#running-without-building)
@@ -355,6 +356,8 @@ export default class PDFToS3Route extends BrowserHTTPRoute {
355
356
  }
356
357
  ```
357
358
 
359
+ On shutdown (SIGTERM, SIGINT, SIGHUP, SIGUSR2, process.on('exit'), `uncaughtException`) browserless will call a `stop` method on all modules. This method is intentionally left blank for SDK extensions to implement. This allows for you to implement any teardown, cleanup, timer clearing, or event unbinding. You don't need to copy over any browserless-core specific teardown as these are handled elsewhere.
360
+
358
361
  With this approach you can effectively write, extend and author your own workflows within browserless!
359
362
 
360
363
  ## Disabling Routes
@@ -397,6 +400,28 @@ Disabling a route will do several things:
397
400
 
398
401
  All of Browserless' internal routes are side-effect free, meaning their largely state-less and don't do exhibit kind of behavior aside from route handling and metrics reporting. Having them in Node's module cache is fine since they're never mounted in the router and set up as a potential route.
399
402
 
403
+ ## Serving Static Files
404
+
405
+ Aside from the static files Browserless serves for documentation, and a few other APIs, SDK projects can also provide static files to be served. To do so, simply create a "static" directory in the root of your project with the files you wish to serve. These will then be served based upon their location in the directory and the file name. Unless that is, of course, you've disabled the static route. Don't be silly.
406
+
407
+ Care should be taken _not_ to create the same filenames that Browserless serves and uses as internal static files takes precedence over SDK files. In short, the list is:
408
+
409
+ - `assets/*`
410
+ - `devtools/*`
411
+ - `docs/*`
412
+ - `function/*`
413
+ - `favicon-32x32.png`
414
+
415
+ Anything else is fair game and will be served properly. An easy way to "scope" files into a path is to simply create a subpath, for instance:
416
+
417
+ `static/enterprise/docs`
418
+
419
+ Will be available to be served under:
420
+
421
+ `http://YOUR-HOST:YOUR-PORT/enterprise/docs`
422
+
423
+ Which prevents this route from colliding with our internal `/docs` route.
424
+
400
425
  ## Running in Development
401
426
 
402
427
  After the project has been set up, you can use npm commands to build and run your code. The most important of these is the `npm run dev` command, which will do the following:
@@ -1,7 +1,9 @@
1
1
  /// <reference types="debug" />
2
2
  /// <reference types="node" />
3
+ /// <reference types="node" />
3
4
  import { BrowserManager, Config, FileSystem, HTTPServer, Limiter, Metrics, Monitoring, Router, Token, WebHooks } from '@browserless.io/browserless';
4
- export declare class Browserless {
5
+ import { EventEmitter } from 'events';
6
+ export declare class Browserless extends EventEmitter {
5
7
  protected debug: debug.Debugger;
6
8
  protected browserManager: BrowserManager;
7
9
  protected config: Config;
@@ -12,6 +14,7 @@ export declare class Browserless {
12
14
  protected router: Router;
13
15
  protected token: Token;
14
16
  protected webhooks: WebHooks;
17
+ protected staticSDKDir: string | null;
15
18
  disabledRouteNames: string[];
16
19
  webSocketRouteFiles: string[];
17
20
  httpRouteFiles: string[];
@@ -32,10 +35,11 @@ export declare class Browserless {
32
35
  protected saveMetrics: () => Promise<void>;
33
36
  setMetricsSaveInterval: (interval: number) => void;
34
37
  private routeIsDisabled;
38
+ setStaticSDKDir(dir: string): void;
35
39
  disableRoutes(...routeNames: string[]): void;
36
40
  addHTTPRoute(httpRouteFilePath: string): void;
37
41
  addWebSocketRoute(webSocketRouteFilePath: string): void;
38
42
  setPort(port: number): void;
39
- stop(): Promise<[void | undefined]>;
43
+ stop(): Promise<[void | undefined, void, void, void, void, void, void, void, void, void]>;
40
44
  start(): Promise<void>;
41
45
  }
@@ -1,9 +1,10 @@
1
1
  import * as path from 'path';
2
2
  import { BrowserManager, ChromeCDP, ChromiumCDP, ChromiumPlaywright, Config, FileSystem, FirefoxPlaywright, HTTPServer, Limiter, Metrics, Monitoring, Router, Token, WebHooks, WebkitPlaywright, availableBrowsers, createLogger, getRouteFiles, makeExternalURL, printLogo, safeParse, } from '@browserless.io/browserless';
3
+ import { EventEmitter } from 'events';
3
4
  import { readFile } from 'fs/promises';
4
5
  import { userInfo } from 'os';
5
6
  const routeSchemas = ['body', 'query'];
6
- export class Browserless {
7
+ export class Browserless extends EventEmitter {
7
8
  debug = createLogger('index');
8
9
  browserManager;
9
10
  config;
@@ -14,6 +15,7 @@ export class Browserless {
14
15
  router;
15
16
  token;
16
17
  webhooks;
18
+ staticSDKDir = null;
17
19
  disabledRouteNames = [];
18
20
  webSocketRouteFiles = [];
19
21
  httpRouteFiles = [];
@@ -21,6 +23,7 @@ export class Browserless {
21
23
  metricsSaveInterval = 5 * 60 * 1000;
22
24
  metricsSaveIntervalID;
23
25
  constructor({ browserManager, config, fileSystem, limiter, metrics, monitoring, router, token, webhooks, } = {}) {
26
+ super();
24
27
  this.config = config || new Config();
25
28
  this.metrics = metrics || new Metrics();
26
29
  this.token = token || new Token(this.config);
@@ -73,6 +76,9 @@ export class Browserless {
73
76
  routeIsDisabled(route) {
74
77
  return this.disabledRouteNames.some((name) => name === route.name);
75
78
  }
79
+ setStaticSDKDir(dir) {
80
+ this.staticSDKDir = dir;
81
+ }
76
82
  disableRoutes(...routeNames) {
77
83
  this.disabledRouteNames.push(...routeNames);
78
84
  }
@@ -90,7 +96,18 @@ export class Browserless {
90
96
  }
91
97
  async stop() {
92
98
  clearInterval(this.metricsSaveIntervalID);
93
- return Promise.all([this.server?.stop()]);
99
+ return Promise.all([
100
+ this.server?.shutdown(),
101
+ this.browserManager.shutdown(),
102
+ this.config.shutdown(),
103
+ this.fileSystem.shutdown(),
104
+ this.limiter.shutdown(),
105
+ this.metrics.shutdown(),
106
+ this.monitoring.shutdown(),
107
+ this.router.shutdown(),
108
+ this.token.shutdown(),
109
+ this.webhooks.shutdown(),
110
+ ]);
94
111
  }
95
112
  async start() {
96
113
  const httpRoutes = [];
@@ -121,7 +138,7 @@ export class Browserless {
121
138
  const routeImport = `${this.config.getIsWin() ? 'file:///' : ''}${httpRoute}`;
122
139
  const logger = createLogger(`http:${name}`);
123
140
  const { default: Route, } = await import(routeImport + `?cb=${Date.now()}`);
124
- const route = new Route(this.browserManager, this.config, this.fileSystem, logger, this.metrics, this.monitoring);
141
+ const route = new Route(this.browserManager, this.config, this.fileSystem, logger, this.metrics, this.monitoring, this.staticSDKDir);
125
142
  if (!this.routeIsDisabled(route)) {
126
143
  route.bodySchema = safeParse(bodySchema);
127
144
  route.querySchema = safeParse(querySchema);
@@ -130,6 +147,7 @@ export class Browserless {
130
147
  route.monitoring = () => this.monitoring;
131
148
  route.fileSystem = () => this.fileSystem;
132
149
  route.debug = () => logger;
150
+ route.staticSDKDir = () => this.staticSDKDir;
133
151
  httpRoutes.push(route);
134
152
  }
135
153
  }
@@ -149,7 +167,7 @@ export class Browserless {
149
167
  const wsImport = `${this.config.getIsWin() ? 'file:///' : ''}${wsRoute}`;
150
168
  const logger = createLogger(`ws:${name}`);
151
169
  const { default: Route, } = await import(wsImport + `?cb=${Date.now()}`);
152
- const route = new Route(this.browserManager, this.config, this.fileSystem, logger, this.metrics, this.monitoring);
170
+ const route = new Route(this.browserManager, this.config, this.fileSystem, logger, this.metrics, this.monitoring, this.staticSDKDir);
153
171
  if (!this.routeIsDisabled(route)) {
154
172
  route.querySchema = safeParse(querySchema);
155
173
  route.config = () => this.config;
@@ -157,6 +175,7 @@ export class Browserless {
157
175
  route.monitoring = () => this.monitoring;
158
176
  route.fileSystem = () => this.fileSystem;
159
177
  route.debug = () => logger;
178
+ route.staticSDKDir = () => this.staticSDKDir;
160
179
  wsRoutes.push(route);
161
180
  }
162
181
  }
@@ -12,7 +12,6 @@ import httpProxy from 'http-proxy';
12
12
  export declare class ChromiumCDP extends EventEmitter {
13
13
  protected config: Config;
14
14
  protected userDataDir: string | null;
15
- protected record: boolean;
16
15
  protected blockAds: boolean;
17
16
  protected running: boolean;
18
17
  protected browser: Browser | null;
@@ -21,16 +20,13 @@ export declare class ChromiumCDP extends EventEmitter {
21
20
  protected debug: import("debug").Debugger;
22
21
  protected proxy: httpProxy<import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>>;
23
22
  protected executablePath: string;
24
- constructor({ userDataDir, config, record, blockAds, }: {
23
+ constructor({ blockAds, config, userDataDir, }: {
25
24
  blockAds: boolean;
26
25
  config: Config;
27
- record: boolean;
28
26
  userDataDir: ChromiumCDP['userDataDir'];
29
27
  });
30
28
  protected cleanListeners(): void;
31
- protected setUpEmbeddedAPI: (page: Page, id: string, record: boolean) => Promise<void>;
32
29
  getPageId: (page: Page) => string;
33
- makeLiveURL: (browserId: string, pageId: string) => string;
34
30
  protected onTargetCreated: (target: Target) => Promise<void>;
35
31
  isRunning: () => boolean;
36
32
  newPage: () => Promise<Page>;
@@ -1,4 +1,4 @@
1
- import { BLESS_PAGE_IDENTIFIER, ServerError, createLogger, encrypt, liveURLSep, makeExternalURL, noop, once, } from '@browserless.io/browserless';
1
+ import { BLESS_PAGE_IDENTIFIER, ServerError, createLogger, 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';
@@ -12,7 +12,6 @@ puppeteerStealth.use(StealthPlugin());
12
12
  export class ChromiumCDP extends EventEmitter {
13
13
  config;
14
14
  userDataDir;
15
- record;
16
15
  blockAds;
17
16
  running = false;
18
17
  browser = null;
@@ -21,11 +20,10 @@ export class ChromiumCDP extends EventEmitter {
21
20
  debug = createLogger('browsers:chromium:cdp');
22
21
  proxy = httpProxy.createProxyServer();
23
22
  executablePath = playwright.chromium.executablePath();
24
- constructor({ userDataDir, config, record, blockAds, }) {
23
+ constructor({ blockAds, config, userDataDir, }) {
25
24
  super();
26
25
  this.userDataDir = userDataDir;
27
26
  this.config = config;
28
- this.record = record;
29
27
  this.blockAds = blockAds;
30
28
  this.debug(`Starting new browser instance`);
31
29
  }
@@ -33,104 +31,10 @@ export class ChromiumCDP extends EventEmitter {
33
31
  this.browser?.removeAllListeners();
34
32
  this.removeAllListeners();
35
33
  }
36
- setUpEmbeddedAPI = async (page, id, record) => {
37
- const pageId = this.getPageId(page);
38
- const liveUrl = this.makeLiveURL(id, pageId);
39
- const embeddedAPI = (pageId, liveUrl, record) => {
40
- Object.defineProperty(window, 'browserless', {
41
- configurable: false,
42
- enumerable: false,
43
- value: {},
44
- writable: false,
45
- });
46
- Object.defineProperties(window.browserless, {
47
- liveUrl: {
48
- configurable: false,
49
- enumerable: false,
50
- value: () => liveUrl,
51
- writable: false,
52
- },
53
- pageId: {
54
- configurable: false,
55
- enumerable: false,
56
- value: () => pageId,
57
- writable: false,
58
- },
59
- startRecording: {
60
- configurable: false,
61
- enumerable: false,
62
- value: (params) => new Promise((resolve, reject) => {
63
- if (!record) {
64
- throw new Error(`Must connect with a record query-param set to "true" in order to use recording`);
65
- }
66
- const start = () => window.postMessage({ ...params, id: pageId, type: 'REC_START' }, '*');
67
- const onStart = (event) => {
68
- if (event.data.id !== pageId)
69
- return;
70
- if (event.data.message === 'REC_STARTED') {
71
- window.removeEventListener('message', onStart);
72
- return resolve(undefined);
73
- }
74
- if (event.data.message === 'REC_START_FAIL') {
75
- window.removeEventListener('message', onStart);
76
- return reject(new Error(event.data.error));
77
- }
78
- };
79
- window.addEventListener('message', onStart);
80
- return document.readyState == 'complete'
81
- ? start()
82
- : window.addEventListener('load', start);
83
- }),
84
- writable: false,
85
- },
86
- stopRecording: {
87
- configurable: false,
88
- enumerable: false,
89
- value: () => new Promise((resolve, reject) => {
90
- if (!record) {
91
- return reject(new Error(`Must connect with a record query-param set to "true" in order to use recording`));
92
- }
93
- const onStop = (event) => {
94
- if (event.data.id !== pageId)
95
- return;
96
- if (event.data.message === 'REC_FILE') {
97
- window.removeEventListener('message', onStop);
98
- return resolve(event.data.file);
99
- }
100
- if (event.data.message === 'REC_STOP_FAIL') {
101
- window.removeEventListener('message', onStop);
102
- return reject(new Error(event.data.error));
103
- }
104
- if (event.data.message === 'REC_NOT_STARTED') {
105
- window.removeEventListener('message', onStop);
106
- return reject(new Error(`Recording hasn't started, did you forget to start it?`));
107
- }
108
- };
109
- window.addEventListener('message', onStop);
110
- return window.postMessage({ id: pageId, type: 'REC_STOP' }, '*');
111
- }),
112
- writable: false,
113
- },
114
- });
115
- };
116
- // Setup the browserless embedded API
117
- await Promise.all([
118
- page.evaluate(embeddedAPI, pageId, liveUrl, record),
119
- page.evaluateOnNewDocument(embeddedAPI, pageId, liveUrl, record),
120
- ]).catch((err) => this.debug(`Error setting up embedded API:`, err.message));
121
- };
122
34
  getPageId = (page) => {
123
35
  // @ts-ignore
124
36
  return page.target()._targetId;
125
37
  };
126
- makeLiveURL = (browserId, pageId) => {
127
- const serverAddress = this.config.getExternalAddress();
128
- const key = this.config.getAESKey();
129
- const path = `${browserId}${liveURLSep}${pageId}`;
130
- const encoded = encrypt(path, key);
131
- const query = `?id=${encoded}`;
132
- return makeExternalURL(serverAddress, 'live', query);
133
- };
134
38
  onTargetCreated = async (target) => {
135
39
  if (target.type() === 'page') {
136
40
  const page = await target.page().catch((e) => {
@@ -155,8 +59,6 @@ export class ChromiumCDP extends EventEmitter {
155
59
  }
156
60
  });
157
61
  }
158
- const browserId = this.wsEndpoint()?.split('/').pop();
159
- await this.setUpEmbeddedAPI(page, browserId, this.record);
160
62
  this.emit('newPage', page);
161
63
  }
162
64
  }
@@ -196,23 +98,11 @@ export class ChromiumCDP extends EventEmitter {
196
98
  ].filter((_) => !!_),
197
99
  executablePath: this.executablePath,
198
100
  };
199
- if (this.record || this.blockAds) {
200
- const requiredExtensionArgs = [];
101
+ if (this.blockAds) {
201
102
  // Necessary to load extensions
202
103
  finalOptions.headless = false;
203
- if (this.record) {
204
- finalOptions.ignoreDefaultArgs = ['--enable-automation'];
205
- requiredExtensionArgs.push('--enable-usermedia-screen-capturing', '--enable-blink-features=GetUserMedia', '--allow-http-screen-capture', '--auto-select-desktop-capture-source=browserless-screencast', '--disable-infobars');
206
- }
207
- const loadExtensionPaths = [
208
- ...(this.record
209
- ? [path.join(__dirname, '..', '..', 'extensions', 'screencast')]
210
- : []),
211
- ...(this.blockAds
212
- ? [path.join(__dirname, '..', '..', 'extensions', 'ublock')]
213
- : []),
214
- ].join(',');
215
- finalOptions.args.push(...requiredExtensionArgs, '--load-extension=' + loadExtensionPaths, '--disable-extensions-except=' + loadExtensionPaths);
104
+ const loadExtensionPaths = path.join(__dirname, '..', '..', 'extensions', 'ublock');
105
+ finalOptions.args.push('--load-extension=' + loadExtensionPaths, '--disable-extensions-except=' + loadExtensionPaths);
216
106
  }
217
107
  const launch = options.stealth
218
108
  ? puppeteerStealth.launch.bind(puppeteerStealth)
@@ -11,16 +11,14 @@ import httpProxy from 'http-proxy';
11
11
  export declare class ChromiumPlaywright extends EventEmitter {
12
12
  protected config: Config;
13
13
  protected userDataDir: string | null;
14
- protected record: boolean;
15
14
  protected running: boolean;
16
15
  protected proxy: httpProxy<import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>>;
17
16
  protected browser: playwright.BrowserServer | null;
18
17
  protected browserWSEndpoint: string | null;
19
18
  protected debug: import("debug").Debugger;
20
19
  protected executablePath: string;
21
- constructor({ config, userDataDir, record, }: {
20
+ constructor({ config, userDataDir, }: {
22
21
  config: Config;
23
- record: boolean;
24
22
  userDataDir: ChromiumPlaywright['userDataDir'];
25
23
  });
26
24
  protected cleanListeners(): void;
@@ -5,18 +5,16 @@ import httpProxy from 'http-proxy';
5
5
  export class ChromiumPlaywright extends EventEmitter {
6
6
  config;
7
7
  userDataDir;
8
- record;
9
8
  running = false;
10
9
  proxy = httpProxy.createProxyServer();
11
10
  browser = null;
12
11
  browserWSEndpoint = null;
13
12
  debug = createLogger('browsers:chromium:playwright');
14
13
  executablePath = playwright.chromium.executablePath();
15
- constructor({ config, userDataDir, record, }) {
14
+ constructor({ config, userDataDir, }) {
16
15
  super();
17
16
  this.userDataDir = userDataDir;
18
17
  this.config = config;
19
- this.record = record;
20
18
  this.debug(`Starting new browser instance`);
21
19
  }
22
20
  cleanListeners() {
@@ -50,9 +48,6 @@ export class ChromiumPlaywright extends EventEmitter {
50
48
  };
51
49
  launch = async (options = {}) => {
52
50
  this.debug(`Launching Playwright Handler`);
53
- if (this.record) {
54
- throw new ServerError(`Recording is not yet available with this browser`);
55
- }
56
51
  this.browser = await playwright.chromium.launchServer({
57
52
  ...options,
58
53
  args: [
@@ -11,15 +11,13 @@ import httpProxy from 'http-proxy';
11
11
  export declare class FirefoxPlaywright extends EventEmitter {
12
12
  protected config: Config;
13
13
  protected userDataDir: string | null;
14
- protected record: boolean;
15
14
  protected running: boolean;
16
15
  protected proxy: httpProxy<import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>>;
17
16
  protected browser: playwright.BrowserServer | null;
18
17
  protected browserWSEndpoint: string | null;
19
18
  protected debug: import("debug").Debugger;
20
- constructor({ config, userDataDir, record, }: {
19
+ constructor({ config, userDataDir, }: {
21
20
  config: Config;
22
- record: boolean;
23
21
  userDataDir: FirefoxPlaywright['userDataDir'];
24
22
  });
25
23
  protected cleanListeners(): void;
@@ -5,17 +5,15 @@ import httpProxy from 'http-proxy';
5
5
  export class FirefoxPlaywright extends EventEmitter {
6
6
  config;
7
7
  userDataDir;
8
- record;
9
8
  running = false;
10
9
  proxy = httpProxy.createProxyServer();
11
10
  browser = null;
12
11
  browserWSEndpoint = null;
13
12
  debug = createLogger('browsers:firefox:playwright');
14
- constructor({ config, userDataDir, record, }) {
13
+ constructor({ config, userDataDir, }) {
15
14
  super();
16
15
  this.userDataDir = userDataDir;
17
16
  this.config = config;
18
- this.record = record;
19
17
  this.debug(`Starting new browser instance`);
20
18
  }
21
19
  cleanListeners() {
@@ -44,9 +42,6 @@ export class FirefoxPlaywright extends EventEmitter {
44
42
  throw new ServerError(`Can't create new page with this browser`);
45
43
  };
46
44
  launch = async (options = {}) => {
47
- if (this.record) {
48
- throw new ServerError(`Recording is not yet available with this browser`);
49
- }
50
45
  this.debug(`Launching Playwright Handler`);
51
46
  this.browser = await playwright.firefox.launchServer({
52
47
  ...options,
@@ -35,5 +35,9 @@ export declare class BrowserManager {
35
35
  getAllSessions: () => Promise<BrowserlessSessionJSON[]>;
36
36
  complete: (browser: BrowserInstance) => Promise<void>;
37
37
  getBrowserForRequest: (req: Request, router: BrowserHTTPRoute | BrowserWebsocketRoute) => Promise<BrowserInstance>;
38
- stop: () => Promise<void>;
38
+ shutdown: () => Promise<void>;
39
+ /**
40
+ * Left blank for downstream SDK modules to optionally implement.
41
+ */
42
+ stop: () => void;
39
43
  }
@@ -42,7 +42,6 @@ export class BrowserManager {
42
42
  const browser = new Browser({
43
43
  blockAds: false,
44
44
  config: this.config,
45
- record: false,
46
45
  userDataDir: null,
47
46
  });
48
47
  await browser.launch();
@@ -70,7 +69,6 @@ export class BrowserManager {
70
69
  const browser = new Browser({
71
70
  blockAds: false,
72
71
  config: this.config,
73
- record: false,
74
72
  userDataDir: null,
75
73
  });
76
74
  await browser.launch();
@@ -217,7 +215,6 @@ export class BrowserManager {
217
215
  };
218
216
  getBrowserForRequest = async (req, router) => {
219
217
  const { browser: Browser } = router;
220
- const record = parseBooleanParam(req.parsed.searchParams, 'record', false);
221
218
  const blockAds = parseBooleanParam(req.parsed.searchParams, 'blockAds', false);
222
219
  const decodedLaunchOptions = convertIfBase64(req.parsed.searchParams.get('launch') || '{}');
223
220
  let parsedLaunchOptions;
@@ -300,7 +297,6 @@ export class BrowserManager {
300
297
  const browser = new Browser({
301
298
  blockAds,
302
299
  config: this.config,
303
- record,
304
300
  userDataDir,
305
301
  });
306
302
  const connectionMeta = {
@@ -324,7 +320,7 @@ export class BrowserManager {
324
320
  });
325
321
  return browser;
326
322
  };
327
- stop = async () => {
323
+ shutdown = async () => {
328
324
  this.debug(`Closing down browser instances`);
329
325
  const sessions = Array.from(this.browsers);
330
326
  await Promise.all(sessions.map(([b]) => b.close()));
@@ -333,6 +329,11 @@ export class BrowserManager {
333
329
  this.timers.forEach((t) => clearTimeout(t));
334
330
  this.browsers = new Map();
335
331
  this.timers = new Map();
332
+ await this.stop();
336
333
  this.debug(`Shutdown complete`);
337
334
  };
335
+ /**
336
+ * Left blank for downstream SDK modules to optionally implement.
337
+ */
338
+ stop = () => { };
338
339
  }
@@ -11,15 +11,13 @@ import httpProxy from 'http-proxy';
11
11
  export declare class WebkitPlaywright extends EventEmitter {
12
12
  protected config: Config;
13
13
  protected userDataDir: string | null;
14
- protected record: boolean;
15
14
  protected running: boolean;
16
15
  protected proxy: httpProxy<import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>>;
17
16
  protected browser: playwright.BrowserServer | null;
18
17
  protected browserWSEndpoint: string | null;
19
18
  protected debug: import("debug").Debugger;
20
- constructor({ config, userDataDir, record, }: {
19
+ constructor({ config, userDataDir, }: {
21
20
  config: Config;
22
- record: boolean;
23
21
  userDataDir: WebkitPlaywright['userDataDir'];
24
22
  });
25
23
  protected cleanListeners(): void;
@@ -5,17 +5,15 @@ import httpProxy from 'http-proxy';
5
5
  export class WebkitPlaywright extends EventEmitter {
6
6
  config;
7
7
  userDataDir;
8
- record;
9
8
  running = false;
10
9
  proxy = httpProxy.createProxyServer();
11
10
  browser = null;
12
11
  browserWSEndpoint = null;
13
12
  debug = createLogger('browsers:webkit:playwright');
14
- constructor({ config, userDataDir, record, }) {
13
+ constructor({ config, userDataDir, }) {
15
14
  super();
16
15
  this.userDataDir = userDataDir;
17
16
  this.config = config;
18
- this.record = record;
19
17
  this.debug(`Starting new browser instance`);
20
18
  }
21
19
  cleanListeners() {
@@ -44,9 +42,6 @@ export class WebkitPlaywright extends EventEmitter {
44
42
  throw new ServerError(`Can't create new page with this browser`);
45
43
  };
46
44
  launch = async (options = {}) => {
47
- if (this.record) {
48
- throw new ServerError(`Recording is not yet available with this browser`);
49
- }
50
45
  this.debug(`Launching Playwright Handler`);
51
46
  this.browser = await playwright.webkit.launchServer({
52
47
  ...options,
package/build/config.d.ts CHANGED
@@ -144,4 +144,13 @@ export declare class Config extends EventEmitter {
144
144
  'Access-Control-Allow-Origin': string;
145
145
  'Access-Control-Max-Age': number;
146
146
  };
147
+ /**
148
+ * Implement any browserless-core-specific shutdown logic here.
149
+ * Calls the empty-SDK stop method for downstream implementations.
150
+ */
151
+ shutdown: () => Promise<void>;
152
+ /**
153
+ * Left blank for downstream SDK modules to optionally implement.
154
+ */
155
+ stop: () => void;
147
156
  }
package/build/config.js CHANGED
@@ -361,4 +361,15 @@ export class Config extends EventEmitter {
361
361
  'Access-Control-Allow-Origin': this.corsOrigin,
362
362
  'Access-Control-Max-Age': this.corsMaxAge,
363
363
  });
364
+ /**
365
+ * Implement any browserless-core-specific shutdown logic here.
366
+ * Calls the empty-SDK stop method for downstream implementations.
367
+ */
368
+ shutdown = async () => {
369
+ await this.stop();
370
+ };
371
+ /**
372
+ * Left blank for downstream SDK modules to optionally implement.
373
+ */
374
+ stop = () => { };
364
375
  }
@@ -1,5 +1,4 @@
1
1
  export declare const encryptionAlgo = "aes-192-cbc";
2
2
  export declare const encryptionSep = ".";
3
- export declare const liveURLSep = ":";
4
3
  export declare const keyLength = 24;
5
4
  export declare const BLESS_PAGE_IDENTIFIER = "BLESS";
@@ -1,5 +1,4 @@
1
1
  export const encryptionAlgo = 'aes-192-cbc';
2
2
  export const encryptionSep = '.';
3
- export const liveURLSep = ':';
4
3
  export const keyLength = 24;
5
4
  export const BLESS_PAGE_IDENTIFIER = 'BLESS';