@browserless.io/browserless 2.4.0-beta-1 → 2.4.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 (100) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/bin/scaffold/README.md +2 -0
  3. package/build/browserless.d.ts +4 -2
  4. package/build/browserless.js +17 -4
  5. package/build/browsers/chromium.cdp.d.ts +1 -5
  6. package/build/browsers/chromium.cdp.js +5 -115
  7. package/build/browsers/chromium.playwright.d.ts +1 -3
  8. package/build/browsers/chromium.playwright.js +1 -6
  9. package/build/browsers/firefox.playwright.d.ts +1 -3
  10. package/build/browsers/firefox.playwright.js +1 -6
  11. package/build/browsers/index.d.ts +5 -1
  12. package/build/browsers/index.js +6 -5
  13. package/build/browsers/webkit.playwright.d.ts +1 -3
  14. package/build/browsers/webkit.playwright.js +1 -6
  15. package/build/config.d.ts +9 -0
  16. package/build/config.js +11 -0
  17. package/build/constants.d.ts +0 -1
  18. package/build/constants.js +0 -1
  19. package/build/file-system.d.ts +12 -1
  20. package/build/file-system.js +14 -1
  21. package/build/http.d.ts +0 -7
  22. package/build/limiter.d.ts +9 -0
  23. package/build/limiter.js +11 -0
  24. package/build/metrics.d.ts +12 -1
  25. package/build/metrics.js +13 -1
  26. package/build/monitoring.d.ts +12 -1
  27. package/build/monitoring.js +14 -1
  28. package/build/router.d.ts +12 -2
  29. package/build/router.js +16 -6
  30. package/build/routes/chrome/http/content.post.body.json +8 -8
  31. package/build/routes/chrome/http/content.post.query.json +0 -4
  32. package/build/routes/chrome/http/download.post.query.json +0 -4
  33. package/build/routes/chrome/http/function.post.query.json +0 -4
  34. package/build/routes/chrome/http/pdf.post.body.json +8 -11
  35. package/build/routes/chrome/http/pdf.post.query.json +0 -4
  36. package/build/routes/chrome/http/performance.post.query.json +0 -4
  37. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  38. package/build/routes/chrome/http/scrape.post.query.json +0 -4
  39. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  40. package/build/routes/chrome/http/screenshot.post.query.json +0 -4
  41. package/build/routes/chrome/ws/browser.query.json +0 -4
  42. package/build/routes/chrome/ws/cdp.query.json +0 -4
  43. package/build/routes/chrome/ws/page.query.json +0 -4
  44. package/build/routes/chrome/ws/playwright.query.json +0 -4
  45. package/build/routes/chromium/http/content.post.body.json +8 -8
  46. package/build/routes/chromium/http/content.post.query.json +0 -4
  47. package/build/routes/chromium/http/download.post.query.json +0 -4
  48. package/build/routes/chromium/http/function.post.query.json +0 -4
  49. package/build/routes/chromium/http/pdf.post.body.json +8 -11
  50. package/build/routes/chromium/http/pdf.post.query.json +0 -4
  51. package/build/routes/chromium/http/performance.post.query.json +0 -4
  52. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  53. package/build/routes/chromium/http/scrape.post.query.json +0 -4
  54. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  55. package/build/routes/chromium/http/screenshot.post.query.json +0 -4
  56. package/build/routes/chromium/ws/browser.query.json +0 -4
  57. package/build/routes/chromium/ws/cdp.query.json +0 -4
  58. package/build/routes/chromium/ws/page.query.json +0 -4
  59. package/build/routes/chromium/ws/playwright.query.json +0 -4
  60. package/build/routes/firefox/ws/playwright.query.json +0 -4
  61. package/build/routes/webkit/ws/playwright.query.json +0 -4
  62. package/build/server.d.ts +8 -3
  63. package/build/server.js +15 -13
  64. package/build/shared/content.http.js +1 -1
  65. package/build/shared/pdf.http.d.ts +0 -1
  66. package/build/shared/pdf.http.js +1 -1
  67. package/build/shared/screenshot.http.js +1 -1
  68. package/build/token.d.ts +12 -1
  69. package/build/token.js +14 -1
  70. package/build/types.d.ts +3 -14
  71. package/build/webhooks.d.ts +12 -1
  72. package/build/webhooks.js +14 -1
  73. package/extensions/.gitkeep +0 -0
  74. package/package.json +8 -8
  75. package/src/browserless.ts +17 -4
  76. package/src/browsers/chromium.cdp.ts +10 -157
  77. package/src/browsers/chromium.playwright.ts +0 -8
  78. package/src/browsers/firefox.playwright.ts +0 -8
  79. package/src/browsers/index.ts +7 -6
  80. package/src/browsers/webkit.playwright.ts +0 -8
  81. package/src/config.ts +13 -0
  82. package/src/constants.ts +0 -1
  83. package/src/file-system.ts +16 -1
  84. package/src/http.ts +0 -8
  85. package/src/limiter.ts +13 -0
  86. package/src/metrics.ts +16 -1
  87. package/src/monitoring.ts +18 -2
  88. package/src/router.ts +20 -9
  89. package/src/server.ts +18 -16
  90. package/src/shared/content.http.ts +5 -4
  91. package/src/shared/pdf.http.ts +4 -5
  92. package/src/shared/screenshot.http.ts +4 -4
  93. package/src/token.ts +18 -2
  94. package/src/types.ts +0 -13
  95. package/src/webhooks.ts +18 -2
  96. package/static/docs/swagger.json +10 -192
  97. package/static/docs/swagger.min.json +9 -191
  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
@@ -27,6 +27,7 @@ import {
27
27
  printLogo,
28
28
  safeParse,
29
29
  } from '@browserless.io/browserless';
30
+ import { EventEmitter } from 'events';
30
31
  import { readFile } from 'fs/promises';
31
32
  import { userInfo } from 'os';
32
33
 
@@ -42,7 +43,7 @@ type routeInstances =
42
43
  | WebSocketRoute
43
44
  | BrowserWebsocketRoute;
44
45
 
45
- export class Browserless {
46
+ export class Browserless extends EventEmitter {
46
47
  protected debug: debug.Debugger = createLogger('index');
47
48
  protected browserManager: BrowserManager;
48
49
  protected config: Config;
@@ -83,6 +84,7 @@ export class Browserless {
83
84
  token?: Browserless['token'];
84
85
  webhooks?: Browserless['webhooks'];
85
86
  } = {}) {
87
+ super();
86
88
  this.config = config || new Config();
87
89
  this.metrics = metrics || new Metrics();
88
90
  this.token = token || new Token(this.config);
@@ -177,7 +179,18 @@ export class Browserless {
177
179
 
178
180
  public async stop() {
179
181
  clearInterval(this.metricsSaveIntervalID as unknown as number);
180
- return Promise.all([this.server?.stop()]);
182
+ return Promise.all([
183
+ this.server?.shutdown(),
184
+ this.browserManager.shutdown(),
185
+ this.config.shutdown(),
186
+ this.fileSystem.shutdown(),
187
+ this.limiter.shutdown(),
188
+ this.metrics.shutdown(),
189
+ this.monitoring.shutdown(),
190
+ this.router.shutdown(),
191
+ this.token.shutdown(),
192
+ this.webhooks.shutdown(),
193
+ ]);
181
194
  }
182
195
 
183
196
  public async start() {
@@ -201,8 +214,8 @@ export class Browserless {
201
214
  this.debug('Starting import of HTTP Routes');
202
215
 
203
216
  for (const httpRoute of [
204
- ...internalHttpRouteFiles,
205
217
  ...this.httpRouteFiles,
218
+ ...internalHttpRouteFiles,
206
219
  ]) {
207
220
  if (httpRoute.endsWith('js')) {
208
221
  const { name } = path.parse(httpRoute);
@@ -251,8 +264,8 @@ export class Browserless {
251
264
 
252
265
  this.debug('Starting import of WebSocket Routes');
253
266
  for (const wsRoute of [
254
- ...internalWsRouteFiles,
255
267
  ...this.webSocketRouteFiles,
268
+ ...internalWsRouteFiles,
256
269
  ]) {
257
270
  if (wsRoute.endsWith('js')) {
258
271
  const { name } = path.parse(wsRoute);
@@ -5,9 +5,6 @@ import {
5
5
  Request,
6
6
  ServerError,
7
7
  createLogger,
8
- encrypt,
9
- liveURLSep,
10
- makeExternalURL,
11
8
  noop,
12
9
  once,
13
10
  } from '@browserless.io/browserless';
@@ -27,7 +24,6 @@ puppeteerStealth.use(StealthPlugin());
27
24
  export class ChromiumCDP extends EventEmitter {
28
25
  protected config: Config;
29
26
  protected userDataDir: string | null;
30
- protected record: boolean;
31
27
  protected blockAds: boolean;
32
28
  protected running = false;
33
29
  protected browser: Browser | null = null;
@@ -38,21 +34,18 @@ export class ChromiumCDP extends EventEmitter {
38
34
  protected executablePath = playwright.chromium.executablePath();
39
35
 
40
36
  constructor({
41
- userDataDir,
42
- config,
43
- record,
44
37
  blockAds,
38
+ config,
39
+ userDataDir,
45
40
  }: {
46
41
  blockAds: boolean;
47
42
  config: Config;
48
- record: boolean;
49
43
  userDataDir: ChromiumCDP['userDataDir'];
50
44
  }) {
51
45
  super();
52
46
 
53
47
  this.userDataDir = userDataDir;
54
48
  this.config = config;
55
- this.record = record;
56
49
  this.blockAds = blockAds;
57
50
  this.debug(`Starting new browser instance`);
58
51
  }
@@ -62,135 +55,11 @@ export class ChromiumCDP extends EventEmitter {
62
55
  this.removeAllListeners();
63
56
  }
64
57
 
65
- protected setUpEmbeddedAPI = async (
66
- page: Page,
67
- id: string,
68
- record: boolean,
69
- ): Promise<void> => {
70
- const pageId = this.getPageId(page);
71
- const liveUrl = this.makeLiveURL(id, pageId);
72
- const embeddedAPI = (pageId: string, liveUrl: string, record: boolean) => {
73
- Object.defineProperty(window, 'browserless', {
74
- configurable: false,
75
- enumerable: false,
76
- value: {},
77
- writable: false,
78
- });
79
-
80
- Object.defineProperties(window.browserless, {
81
- liveUrl: {
82
- configurable: false,
83
- enumerable: false,
84
- value: () => liveUrl,
85
- writable: false,
86
- },
87
- pageId: {
88
- configurable: false,
89
- enumerable: false,
90
- value: () => pageId,
91
- writable: false,
92
- },
93
- startRecording: {
94
- configurable: false,
95
- enumerable: false,
96
- value: (params: object) =>
97
- new Promise((resolve, reject) => {
98
- if (!record) {
99
- throw new Error(
100
- `Must connect with a record query-param set to "true" in order to use recording`,
101
- );
102
- }
103
- const start = () =>
104
- window.postMessage(
105
- { ...params, id: pageId, type: 'REC_START' },
106
- '*',
107
- );
108
- const onStart = (event: MessageEvent) => {
109
- if (event.data.id !== pageId) return;
110
- if (event.data.message === 'REC_STARTED') {
111
- window.removeEventListener('message', onStart);
112
- return resolve(undefined);
113
- }
114
- if (event.data.message === 'REC_START_FAIL') {
115
- window.removeEventListener('message', onStart);
116
- return reject(new Error(event.data.error));
117
- }
118
- };
119
-
120
- window.addEventListener('message', onStart);
121
-
122
- return document.readyState == 'complete'
123
- ? start()
124
- : window.addEventListener('load', start);
125
- }),
126
- writable: false,
127
- },
128
- stopRecording: {
129
- configurable: false,
130
- enumerable: false,
131
- value: () =>
132
- new Promise((resolve, reject) => {
133
- if (!record) {
134
- return reject(
135
- new Error(
136
- `Must connect with a record query-param set to "true" in order to use recording`,
137
- ),
138
- );
139
- }
140
- const onStop = (event: MessageEvent) => {
141
- if (event.data.id !== pageId) return;
142
- if (event.data.message === 'REC_FILE') {
143
- window.removeEventListener('message', onStop);
144
- return resolve(event.data.file);
145
- }
146
-
147
- if (event.data.message === 'REC_STOP_FAIL') {
148
- window.removeEventListener('message', onStop);
149
- return reject(new Error(event.data.error));
150
- }
151
-
152
- if (event.data.message === 'REC_NOT_STARTED') {
153
- window.removeEventListener('message', onStop);
154
- return reject(
155
- new Error(
156
- `Recording hasn't started, did you forget to start it?`,
157
- ),
158
- );
159
- }
160
- };
161
-
162
- window.addEventListener('message', onStop);
163
- return window.postMessage({ id: pageId, type: 'REC_STOP' }, '*');
164
- }),
165
- writable: false,
166
- },
167
- });
168
- };
169
-
170
- // Setup the browserless embedded API
171
- await Promise.all([
172
- page.evaluate(embeddedAPI, pageId, liveUrl, record),
173
- page.evaluateOnNewDocument(embeddedAPI, pageId, liveUrl, record),
174
- ]).catch((err) =>
175
- this.debug(`Error setting up embedded API:`, err.message),
176
- );
177
- };
178
-
179
58
  public getPageId = (page: Page): string => {
180
59
  // @ts-ignore
181
60
  return page.target()._targetId;
182
61
  };
183
62
 
184
- public makeLiveURL = (browserId: string, pageId: string) => {
185
- const serverAddress = this.config.getExternalAddress();
186
- const key = this.config.getAESKey();
187
- const path = `${browserId}${liveURLSep}${pageId}`;
188
- const encoded = encrypt(path, key);
189
- const query = `?id=${encoded}`;
190
-
191
- return makeExternalURL(serverAddress, 'live', query);
192
- };
193
-
194
63
  protected onTargetCreated = async (target: Target) => {
195
64
  if (target.type() === 'page') {
196
65
  const page = await target.page().catch((e) => {
@@ -219,8 +88,6 @@ export class ChromiumCDP extends EventEmitter {
219
88
  }
220
89
  });
221
90
  }
222
- const browserId = this.wsEndpoint()?.split('/').pop() as string;
223
- await this.setUpEmbeddedAPI(page, browserId, this.record);
224
91
  this.emit('newPage', page);
225
92
  }
226
93
  }
@@ -268,33 +135,19 @@ export class ChromiumCDP extends EventEmitter {
268
135
  executablePath: this.executablePath,
269
136
  };
270
137
 
271
- if (this.record || this.blockAds) {
272
- const requiredExtensionArgs: string[] = [];
138
+ if (this.blockAds) {
273
139
  // Necessary to load extensions
274
140
  finalOptions.headless = false;
275
141
 
276
- if (this.record) {
277
- finalOptions.ignoreDefaultArgs = ['--enable-automation'];
278
- requiredExtensionArgs.push(
279
- '--enable-usermedia-screen-capturing',
280
- '--enable-blink-features=GetUserMedia',
281
- '--allow-http-screen-capture',
282
- '--auto-select-desktop-capture-source=browserless-screencast',
283
- '--disable-infobars',
284
- );
285
- }
286
-
287
- const loadExtensionPaths: string = [
288
- ...(this.record
289
- ? [path.join(__dirname, '..', '..', 'extensions', 'screencast')]
290
- : []),
291
- ...(this.blockAds
292
- ? [path.join(__dirname, '..', '..', 'extensions', 'ublock')]
293
- : []),
294
- ].join(',');
142
+ const loadExtensionPaths: string = path.join(
143
+ __dirname,
144
+ '..',
145
+ '..',
146
+ 'extensions',
147
+ 'ublock',
148
+ );
295
149
 
296
150
  finalOptions.args.push(
297
- ...requiredExtensionArgs,
298
151
  '--load-extension=' + loadExtensionPaths,
299
152
  '--disable-extensions-except=' + loadExtensionPaths,
300
153
  );
@@ -13,7 +13,6 @@ import httpProxy from 'http-proxy';
13
13
  export class ChromiumPlaywright extends EventEmitter {
14
14
  protected config: Config;
15
15
  protected userDataDir: string | null;
16
- protected record: boolean;
17
16
  protected running = false;
18
17
  protected proxy = httpProxy.createProxyServer();
19
18
  protected browser: playwright.BrowserServer | null = null;
@@ -24,17 +23,14 @@ export class ChromiumPlaywright extends EventEmitter {
24
23
  constructor({
25
24
  config,
26
25
  userDataDir,
27
- record,
28
26
  }: {
29
27
  config: Config;
30
- record: boolean;
31
28
  userDataDir: ChromiumPlaywright['userDataDir'];
32
29
  }) {
33
30
  super();
34
31
 
35
32
  this.userDataDir = userDataDir;
36
33
  this.config = config;
37
- this.record = record;
38
34
 
39
35
  this.debug(`Starting new browser instance`);
40
36
  }
@@ -82,10 +78,6 @@ export class ChromiumPlaywright extends EventEmitter {
82
78
  ): Promise<playwright.BrowserServer> => {
83
79
  this.debug(`Launching Playwright Handler`);
84
80
 
85
- if (this.record) {
86
- throw new ServerError(`Recording is not yet available with this browser`);
87
- }
88
-
89
81
  this.browser = await playwright.chromium.launchServer({
90
82
  ...options,
91
83
  args: [
@@ -13,7 +13,6 @@ import httpProxy from 'http-proxy';
13
13
  export class FirefoxPlaywright extends EventEmitter {
14
14
  protected config: Config;
15
15
  protected userDataDir: string | null;
16
- protected record: boolean;
17
16
  protected running = false;
18
17
  protected proxy = httpProxy.createProxyServer();
19
18
  protected browser: playwright.BrowserServer | null = null;
@@ -23,17 +22,14 @@ export class FirefoxPlaywright extends EventEmitter {
23
22
  constructor({
24
23
  config,
25
24
  userDataDir,
26
- record,
27
25
  }: {
28
26
  config: Config;
29
- record: boolean;
30
27
  userDataDir: FirefoxPlaywright['userDataDir'];
31
28
  }) {
32
29
  super();
33
30
 
34
31
  this.userDataDir = userDataDir;
35
32
  this.config = config;
36
- this.record = record;
37
33
 
38
34
  this.debug(`Starting new browser instance`);
39
35
  }
@@ -73,10 +69,6 @@ export class FirefoxPlaywright extends EventEmitter {
73
69
  public launch = async (
74
70
  options: BrowserServerOptions = {},
75
71
  ): Promise<playwright.BrowserServer> => {
76
- if (this.record) {
77
- throw new ServerError(`Recording is not yet available with this browser`);
78
- }
79
-
80
72
  this.debug(`Launching Playwright Handler`);
81
73
 
82
74
  this.browser = await playwright.firefox.launchServer({
@@ -84,7 +84,6 @@ export class BrowserManager {
84
84
  const browser = new Browser({
85
85
  blockAds: false,
86
86
  config: this.config,
87
- record: false,
88
87
  userDataDir: null,
89
88
  });
90
89
  await browser.launch();
@@ -122,7 +121,6 @@ export class BrowserManager {
122
121
  const browser = new Browser({
123
122
  blockAds: false,
124
123
  config: this.config,
125
- record: false,
126
124
  userDataDir: null,
127
125
  });
128
126
  await browser.launch();
@@ -322,7 +320,6 @@ export class BrowserManager {
322
320
  router: BrowserHTTPRoute | BrowserWebsocketRoute,
323
321
  ): Promise<BrowserInstance> => {
324
322
  const { browser: Browser } = router;
325
- const record = parseBooleanParam(req.parsed.searchParams, 'record', false);
326
323
  const blockAds = parseBooleanParam(
327
324
  req.parsed.searchParams,
328
325
  'blockAds',
@@ -446,7 +443,6 @@ export class BrowserManager {
446
443
  const browser = new Browser({
447
444
  blockAds,
448
445
  config: this.config,
449
- record,
450
446
  userDataDir,
451
447
  });
452
448
 
@@ -477,7 +473,7 @@ export class BrowserManager {
477
473
  return browser;
478
474
  };
479
475
 
480
- public stop = async (): Promise<void> => {
476
+ public shutdown = async (): Promise<void> => {
481
477
  this.debug(`Closing down browser instances`);
482
478
  const sessions = Array.from(this.browsers);
483
479
  await Promise.all(sessions.map(([b]) => b.close()));
@@ -486,7 +482,12 @@ export class BrowserManager {
486
482
  this.timers.forEach((t) => clearTimeout(t));
487
483
  this.browsers = new Map();
488
484
  this.timers = new Map();
489
-
485
+ await this.stop();
490
486
  this.debug(`Shutdown complete`);
491
487
  };
488
+
489
+ /**
490
+ * Left blank for downstream SDK modules to optionally implement.
491
+ */
492
+ public stop = () => {};
492
493
  }
@@ -13,7 +13,6 @@ import httpProxy from 'http-proxy';
13
13
  export class WebkitPlaywright extends EventEmitter {
14
14
  protected config: Config;
15
15
  protected userDataDir: string | null;
16
- protected record: boolean;
17
16
  protected running = false;
18
17
  protected proxy = httpProxy.createProxyServer();
19
18
  protected browser: playwright.BrowserServer | null = null;
@@ -23,17 +22,14 @@ export class WebkitPlaywright extends EventEmitter {
23
22
  constructor({
24
23
  config,
25
24
  userDataDir,
26
- record,
27
25
  }: {
28
26
  config: Config;
29
- record: boolean;
30
27
  userDataDir: WebkitPlaywright['userDataDir'];
31
28
  }) {
32
29
  super();
33
30
 
34
31
  this.userDataDir = userDataDir;
35
32
  this.config = config;
36
- this.record = record;
37
33
 
38
34
  this.debug(`Starting new browser instance`);
39
35
  }
@@ -73,10 +69,6 @@ export class WebkitPlaywright extends EventEmitter {
73
69
  public launch = async (
74
70
  options: BrowserServerOptions = {},
75
71
  ): Promise<playwright.BrowserServer> => {
76
- if (this.record) {
77
- throw new ServerError(`Recording is not yet available with this browser`);
78
- }
79
-
80
72
  this.debug(`Launching Playwright Handler`);
81
73
 
82
74
  this.browser = await playwright.webkit.launchServer({
package/src/config.ts CHANGED
@@ -441,4 +441,17 @@ export class Config extends EventEmitter {
441
441
  'Access-Control-Allow-Origin': this.corsOrigin,
442
442
  'Access-Control-Max-Age': this.corsMaxAge,
443
443
  });
444
+
445
+ /**
446
+ * Implement any browserless-core-specific shutdown logic here.
447
+ * Calls the empty-SDK stop method for downstream implementations.
448
+ */
449
+ public shutdown = async () => {
450
+ await this.stop();
451
+ };
452
+
453
+ /**
454
+ * Left blank for downstream SDK modules to optionally implement.
455
+ */
456
+ public stop = () => {};
444
457
  }
package/src/constants.ts CHANGED
@@ -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';
@@ -5,13 +5,15 @@ import {
5
5
  encrypt,
6
6
  } from '@browserless.io/browserless';
7
7
  import { readFile, writeFile } from 'fs/promises';
8
+ import { EventEmitter } from 'events';
8
9
 
9
- export class FileSystem {
10
+ export class FileSystem extends EventEmitter {
10
11
  protected fsMap: Map<string, string[]> = new Map();
11
12
  protected currentAESKey: Buffer;
12
13
  protected log = createLogger('file-system');
13
14
 
14
15
  constructor(protected config: Config) {
16
+ super();
15
17
  this.currentAESKey = config.getAESKey();
16
18
  this.config.on('token', this.handleTokenChange);
17
19
  }
@@ -79,4 +81,17 @@ export class FileSystem {
79
81
 
80
82
  return splitContents;
81
83
  };
84
+
85
+ /**
86
+ * Implement any browserless-core-specific shutdown logic here.
87
+ * Calls the empty-SDK stop method for downstream implementations.
88
+ */
89
+ public shutdown = async () => {
90
+ await this.stop();
91
+ };
92
+
93
+ /**
94
+ * Left blank for downstream SDK modules to optionally implement.
95
+ */
96
+ public stop = () => {};
82
97
  }
package/src/http.ts CHANGED
@@ -161,14 +161,6 @@ export interface SystemQueryParameters {
161
161
  */
162
162
  launch?: CDPLaunchOptions | BrowserServerOptions | string;
163
163
 
164
- /**
165
- * Whether or nor to record the session. The browser will run
166
- * in "head-full" mode, and recording is started and closed
167
- * via the embedded browserless API. Please refer to the "Recording"
168
- * section in the live documentation site for more details.
169
- */
170
- record?: boolean;
171
-
172
164
  /**
173
165
  * Override the system-level timeout for this request.
174
166
  * Accepts a value in milliseconds.
package/src/limiter.ts CHANGED
@@ -226,4 +226,17 @@ export class Limiter extends q {
226
226
  return bound;
227
227
  });
228
228
  };
229
+
230
+ /**
231
+ * Implement any browserless-core-specific shutdown logic here.
232
+ * Calls the empty-SDK stop method for downstream implementations.
233
+ */
234
+ public shutdown = async () => {
235
+ await this.stop();
236
+ };
237
+
238
+ /**
239
+ * Left blank for downstream SDK modules to optionally implement.
240
+ */
241
+ public stop = () => {};
229
242
  }
package/src/metrics.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { IBrowserlessStats } from '@browserless.io/browserless';
2
2
 
3
- export class Metrics {
3
+ import { EventEmitter } from 'events';
4
+
5
+ export class Metrics extends EventEmitter {
4
6
  protected sessionTimes: number[] = [];
5
7
  protected successful = 0;
6
8
  protected queued = 0;
@@ -105,4 +107,17 @@ export class Metrics {
105
107
  ),
106
108
  };
107
109
  }
110
+
111
+ /**
112
+ * Implement any browserless-core-specific shutdown logic here.
113
+ * Calls the empty-SDK stop method for downstream implementations.
114
+ */
115
+ public shutdown = async () => {
116
+ await this.stop();
117
+ };
118
+
119
+ /**
120
+ * Left blank for downstream SDK modules to optionally implement.
121
+ */
122
+ public stop = () => {};
108
123
  }
package/src/monitoring.ts CHANGED
@@ -3,11 +3,14 @@ import {
3
3
  IResourceLoad,
4
4
  createLogger,
5
5
  } from '@browserless.io/browserless';
6
+ import { EventEmitter } from 'events';
6
7
  import si from 'systeminformation';
7
8
 
8
- export class Monitoring {
9
+ export class Monitoring extends EventEmitter {
9
10
  protected log = createLogger('hardware');
10
- constructor(protected config: Config) {}
11
+ constructor(protected config: Config) {
12
+ super();
13
+ }
11
14
 
12
15
  public getMachineStats = async (): Promise<IResourceLoad> => {
13
16
  const [cpuLoad, memLoad] = await Promise.all([
@@ -50,4 +53,17 @@ export class Monitoring {
50
53
  memoryOverloaded,
51
54
  };
52
55
  };
56
+
57
+ /**
58
+ * Implement any browserless-core-specific shutdown logic here.
59
+ * Calls the empty-SDK stop method for downstream implementations.
60
+ */
61
+ public shutdown = async () => {
62
+ await this.stop();
63
+ };
64
+
65
+ /**
66
+ * Left blank for downstream SDK modules to optionally implement.
67
+ */
68
+ public stop = () => {};
53
69
  }
package/src/router.ts CHANGED
@@ -16,10 +16,11 @@ import {
16
16
  isConnected,
17
17
  writeResponse,
18
18
  } from '@browserless.io/browserless';
19
+ import { EventEmitter } from 'events';
19
20
  import micromatch from 'micromatch';
20
21
  import stream from 'stream';
21
22
 
22
- export class Router {
23
+ export class Router extends EventEmitter {
23
24
  protected log = createLogger('router');
24
25
  protected verbose = createLogger('router:verbose');
25
26
  protected httpRoutes: Array<HTTPRoute | BrowserHTTPRoute> = [];
@@ -29,7 +30,9 @@ export class Router {
29
30
  protected config: Config,
30
31
  protected browserManager: BrowserManager,
31
32
  protected limiter: Limiter,
32
- ) {}
33
+ ) {
34
+ super();
35
+ }
33
36
 
34
37
  protected getTimeout(req: Request) {
35
38
  const timer = req.parsed.searchParams.get('timeout');
@@ -207,13 +210,6 @@ export class Router {
207
210
  return route;
208
211
  }
209
212
 
210
- public teardown() {
211
- this.httpRoutes = [];
212
- this.webSocketRoutes = [];
213
-
214
- return this.browserManager.stop();
215
- }
216
-
217
213
  public getStaticHandler() {
218
214
  return this.httpRoutes.find((route) =>
219
215
  route.path.includes(HTTPManagementRoutes.static),
@@ -253,4 +249,19 @@ export class Router {
253
249
  (r.path as Array<PathTypes>).some((p) => micromatch.isMatch(pathname, p)),
254
250
  );
255
251
  }
252
+
253
+ /**
254
+ * Implement any browserless-core-specific shutdown logic here.
255
+ * Calls the empty-SDK stop method for downstream implementations.
256
+ */
257
+ public shutdown = async () => {
258
+ this.httpRoutes = [];
259
+ this.webSocketRoutes = [];
260
+ await this.stop();
261
+ };
262
+
263
+ /**
264
+ * Left blank for downstream SDK modules to optionally implement.
265
+ */
266
+ public stop = () => {};
256
267
  }