@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.
- package/CHANGELOG.md +14 -1
- package/bin/scaffold/README.md +2 -0
- package/build/browserless.d.ts +4 -2
- package/build/browserless.js +17 -4
- package/build/browsers/chromium.cdp.d.ts +1 -5
- package/build/browsers/chromium.cdp.js +5 -115
- package/build/browsers/chromium.playwright.d.ts +1 -3
- package/build/browsers/chromium.playwright.js +1 -6
- package/build/browsers/firefox.playwright.d.ts +1 -3
- package/build/browsers/firefox.playwright.js +1 -6
- package/build/browsers/index.d.ts +5 -1
- package/build/browsers/index.js +6 -5
- package/build/browsers/webkit.playwright.d.ts +1 -3
- package/build/browsers/webkit.playwright.js +1 -6
- package/build/config.d.ts +9 -0
- package/build/config.js +11 -0
- package/build/constants.d.ts +0 -1
- package/build/constants.js +0 -1
- package/build/file-system.d.ts +12 -1
- package/build/file-system.js +14 -1
- package/build/http.d.ts +0 -7
- package/build/limiter.d.ts +9 -0
- package/build/limiter.js +11 -0
- package/build/metrics.d.ts +12 -1
- package/build/metrics.js +13 -1
- package/build/monitoring.d.ts +12 -1
- package/build/monitoring.js +14 -1
- package/build/router.d.ts +12 -2
- package/build/router.js +16 -6
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/content.post.query.json +0 -4
- package/build/routes/chrome/http/download.post.query.json +0 -4
- package/build/routes/chrome/http/function.post.query.json +0 -4
- package/build/routes/chrome/http/pdf.post.body.json +8 -11
- package/build/routes/chrome/http/pdf.post.query.json +0 -4
- package/build/routes/chrome/http/performance.post.query.json +0 -4
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/scrape.post.query.json +0 -4
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.query.json +0 -4
- package/build/routes/chrome/ws/browser.query.json +0 -4
- package/build/routes/chrome/ws/cdp.query.json +0 -4
- package/build/routes/chrome/ws/page.query.json +0 -4
- package/build/routes/chrome/ws/playwright.query.json +0 -4
- package/build/routes/chromium/http/content.post.body.json +8 -8
- package/build/routes/chromium/http/content.post.query.json +0 -4
- package/build/routes/chromium/http/download.post.query.json +0 -4
- package/build/routes/chromium/http/function.post.query.json +0 -4
- package/build/routes/chromium/http/pdf.post.body.json +8 -11
- package/build/routes/chromium/http/pdf.post.query.json +0 -4
- package/build/routes/chromium/http/performance.post.query.json +0 -4
- package/build/routes/chromium/http/scrape.post.body.json +8 -8
- package/build/routes/chromium/http/scrape.post.query.json +0 -4
- package/build/routes/chromium/http/screenshot.post.body.json +8 -8
- package/build/routes/chromium/http/screenshot.post.query.json +0 -4
- package/build/routes/chromium/ws/browser.query.json +0 -4
- package/build/routes/chromium/ws/cdp.query.json +0 -4
- package/build/routes/chromium/ws/page.query.json +0 -4
- package/build/routes/chromium/ws/playwright.query.json +0 -4
- package/build/routes/firefox/ws/playwright.query.json +0 -4
- package/build/routes/webkit/ws/playwright.query.json +0 -4
- package/build/server.d.ts +8 -3
- package/build/server.js +15 -13
- package/build/shared/content.http.js +1 -1
- package/build/shared/pdf.http.d.ts +0 -1
- package/build/shared/pdf.http.js +1 -1
- package/build/shared/screenshot.http.js +1 -1
- package/build/token.d.ts +12 -1
- package/build/token.js +14 -1
- package/build/types.d.ts +3 -14
- package/build/webhooks.d.ts +12 -1
- package/build/webhooks.js +14 -1
- package/extensions/.gitkeep +0 -0
- package/package.json +8 -8
- package/src/browserless.ts +17 -4
- package/src/browsers/chromium.cdp.ts +10 -157
- package/src/browsers/chromium.playwright.ts +0 -8
- package/src/browsers/firefox.playwright.ts +0 -8
- package/src/browsers/index.ts +7 -6
- package/src/browsers/webkit.playwright.ts +0 -8
- package/src/config.ts +13 -0
- package/src/constants.ts +0 -1
- package/src/file-system.ts +16 -1
- package/src/http.ts +0 -8
- package/src/limiter.ts +13 -0
- package/src/metrics.ts +16 -1
- package/src/monitoring.ts +18 -2
- package/src/router.ts +20 -9
- package/src/server.ts +18 -16
- package/src/shared/content.http.ts +5 -4
- package/src/shared/pdf.http.ts +4 -5
- package/src/shared/screenshot.http.ts +4 -4
- package/src/token.ts +18 -2
- package/src/types.ts +0 -13
- package/src/webhooks.ts +18 -2
- package/static/docs/swagger.json +10 -192
- package/static/docs/swagger.min.json +9 -191
- package/extensions/screencast/background.js +0 -143
- package/extensions/screencast/content_script.js +0 -18
- package/extensions/screencast/manifest.json +0 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
# [Latest](https://github.com/browserless/chrome/compare/v2.
|
|
1
|
+
# [Latest](https://github.com/browserless/chrome/compare/v2.4.0...main)
|
|
2
|
+
- Dependency updates.
|
|
3
|
+
|
|
4
|
+
# [v2.4.0](https://github.com/browserless/chrome/compare/v2.3.0...v2.4.0)
|
|
5
|
+
**Potentially Breaking**
|
|
6
|
+
- Drops support for recording and screencasting in favor of library-based approaches.
|
|
7
|
+
- `playwright@1.42.1` and `puppeteer@22.4.0`.
|
|
8
|
+
|
|
9
|
+
**Other Changes**
|
|
10
|
+
- SDK routes now take precedence over core routes when a path-collision occurs.
|
|
11
|
+
- Support SDK projects with their own `static` directories.
|
|
12
|
+
- Adds support for user-specified `stop` methods in SDK Module extensions.
|
|
13
|
+
- Core modules now extend `EventEmitter`, making them able to publish events.
|
|
14
|
+
- Fixes issues with `addStyleTag` happening before `goto` calls in REST APIs.
|
|
2
15
|
- Dependency updates.
|
|
3
16
|
|
|
4
17
|
# [v2.3.0](https://github.com/browserless/chrome/compare/v2.2.0...v2.3.0)
|
package/bin/scaffold/README.md
CHANGED
|
@@ -356,6 +356,8 @@ export default class PDFToS3Route extends BrowserHTTPRoute {
|
|
|
356
356
|
}
|
|
357
357
|
```
|
|
358
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
|
+
|
|
359
361
|
With this approach you can effectively write, extend and author your own workflows within browserless!
|
|
360
362
|
|
|
361
363
|
## Disabling Routes
|
package/build/browserless.d.ts
CHANGED
|
@@ -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
|
-
|
|
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;
|
|
@@ -38,6 +40,6 @@ export declare class Browserless {
|
|
|
38
40
|
addHTTPRoute(httpRouteFilePath: string): void;
|
|
39
41
|
addWebSocketRoute(webSocketRouteFilePath: string): void;
|
|
40
42
|
setPort(port: number): void;
|
|
41
|
-
stop(): Promise<[void | undefined]>;
|
|
43
|
+
stop(): Promise<[void | undefined, void, void, void, void, void, void, void, void, void]>;
|
|
42
44
|
start(): Promise<void>;
|
|
43
45
|
}
|
package/build/browserless.js
CHANGED
|
@@ -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;
|
|
@@ -22,6 +23,7 @@ export class Browserless {
|
|
|
22
23
|
metricsSaveInterval = 5 * 60 * 1000;
|
|
23
24
|
metricsSaveIntervalID;
|
|
24
25
|
constructor({ browserManager, config, fileSystem, limiter, metrics, monitoring, router, token, webhooks, } = {}) {
|
|
26
|
+
super();
|
|
25
27
|
this.config = config || new Config();
|
|
26
28
|
this.metrics = metrics || new Metrics();
|
|
27
29
|
this.token = token || new Token(this.config);
|
|
@@ -94,7 +96,18 @@ export class Browserless {
|
|
|
94
96
|
}
|
|
95
97
|
async stop() {
|
|
96
98
|
clearInterval(this.metricsSaveIntervalID);
|
|
97
|
-
return Promise.all([
|
|
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
|
+
]);
|
|
98
111
|
}
|
|
99
112
|
async start() {
|
|
100
113
|
const httpRoutes = [];
|
|
@@ -112,8 +125,8 @@ export class Browserless {
|
|
|
112
125
|
this.debug(`Running as user "${userInfo().username}"`);
|
|
113
126
|
this.debug('Starting import of HTTP Routes');
|
|
114
127
|
for (const httpRoute of [
|
|
115
|
-
...internalHttpRouteFiles,
|
|
116
128
|
...this.httpRouteFiles,
|
|
129
|
+
...internalHttpRouteFiles,
|
|
117
130
|
]) {
|
|
118
131
|
if (httpRoute.endsWith('js')) {
|
|
119
132
|
const { name } = path.parse(httpRoute);
|
|
@@ -141,8 +154,8 @@ export class Browserless {
|
|
|
141
154
|
}
|
|
142
155
|
this.debug('Starting import of WebSocket Routes');
|
|
143
156
|
for (const wsRoute of [
|
|
144
|
-
...internalWsRouteFiles,
|
|
145
157
|
...this.webSocketRouteFiles,
|
|
158
|
+
...internalWsRouteFiles,
|
|
146
159
|
]) {
|
|
147
160
|
if (wsRoute.endsWith('js')) {
|
|
148
161
|
const { name } = path.parse(wsRoute);
|
|
@@ -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({
|
|
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,
|
|
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({
|
|
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.
|
|
200
|
-
const requiredExtensionArgs = [];
|
|
101
|
+
if (this.blockAds) {
|
|
201
102
|
// Necessary to load extensions
|
|
202
103
|
finalOptions.headless = false;
|
|
203
|
-
|
|
204
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
38
|
+
shutdown: () => Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Left blank for downstream SDK modules to optionally implement.
|
|
41
|
+
*/
|
|
42
|
+
stop: () => void;
|
|
39
43
|
}
|
package/build/browsers/index.js
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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,
|
|
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
|
}
|
package/build/constants.d.ts
CHANGED
package/build/constants.js
CHANGED
package/build/file-system.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="debug" />
|
|
3
|
+
/// <reference types="node" />
|
|
3
4
|
import { Config } from '@browserless.io/browserless';
|
|
4
|
-
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
export declare class FileSystem extends EventEmitter {
|
|
5
7
|
protected config: Config;
|
|
6
8
|
protected fsMap: Map<string, string[]>;
|
|
7
9
|
protected currentAESKey: Buffer;
|
|
@@ -26,4 +28,13 @@ export declare class FileSystem {
|
|
|
26
28
|
* @returns Promise of the contents separated by newlines
|
|
27
29
|
*/
|
|
28
30
|
read: (path: string) => Promise<string[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Implement any browserless-core-specific shutdown logic here.
|
|
33
|
+
* Calls the empty-SDK stop method for downstream implementations.
|
|
34
|
+
*/
|
|
35
|
+
shutdown: () => Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Left blank for downstream SDK modules to optionally implement.
|
|
38
|
+
*/
|
|
39
|
+
stop: () => void;
|
|
29
40
|
}
|
package/build/file-system.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { createLogger, decrypt, encrypt, } from '@browserless.io/browserless';
|
|
2
2
|
import { readFile, writeFile } from 'fs/promises';
|
|
3
|
-
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
export class FileSystem extends EventEmitter {
|
|
4
5
|
config;
|
|
5
6
|
fsMap = new Map();
|
|
6
7
|
currentAESKey;
|
|
7
8
|
log = createLogger('file-system');
|
|
8
9
|
constructor(config) {
|
|
10
|
+
super();
|
|
9
11
|
this.config = config;
|
|
10
12
|
this.currentAESKey = config.getAESKey();
|
|
11
13
|
this.config.on('token', this.handleTokenChange);
|
|
@@ -58,4 +60,15 @@ export class FileSystem {
|
|
|
58
60
|
this.fsMap.set(path, splitContents);
|
|
59
61
|
return splitContents;
|
|
60
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Implement any browserless-core-specific shutdown logic here.
|
|
65
|
+
* Calls the empty-SDK stop method for downstream implementations.
|
|
66
|
+
*/
|
|
67
|
+
shutdown = async () => {
|
|
68
|
+
await this.stop();
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Left blank for downstream SDK modules to optionally implement.
|
|
72
|
+
*/
|
|
73
|
+
stop = () => { };
|
|
61
74
|
}
|
package/build/http.d.ts
CHANGED
|
@@ -181,13 +181,6 @@ export interface SystemQueryParameters {
|
|
|
181
181
|
* object, or a base64-encoded JSON object.
|
|
182
182
|
*/
|
|
183
183
|
launch?: CDPLaunchOptions | BrowserServerOptions | string;
|
|
184
|
-
/**
|
|
185
|
-
* Whether or nor to record the session. The browser will run
|
|
186
|
-
* in "head-full" mode, and recording is started and closed
|
|
187
|
-
* via the embedded browserless API. Please refer to the "Recording"
|
|
188
|
-
* section in the live documentation site for more details.
|
|
189
|
-
*/
|
|
190
|
-
record?: boolean;
|
|
191
184
|
/**
|
|
192
185
|
* Override the system-level timeout for this request.
|
|
193
186
|
* Accepts a value in milliseconds.
|
package/build/limiter.d.ts
CHANGED
|
@@ -44,5 +44,14 @@ export declare class Limiter extends q {
|
|
|
44
44
|
get concurrencySize(): number;
|
|
45
45
|
get hasCapacity(): boolean;
|
|
46
46
|
limit: <TArgs extends unknown[], TResult>(limitFn: LimitFn<TArgs, TResult>, overCapacityFn: ErrorFn<TArgs>, onTimeoutFn: ErrorFn<TArgs>, timeoutOverrideFn: (...args: TArgs) => number | undefined) => LimitFn<TArgs, unknown>;
|
|
47
|
+
/**
|
|
48
|
+
* Implement any browserless-core-specific shutdown logic here.
|
|
49
|
+
* Calls the empty-SDK stop method for downstream implementations.
|
|
50
|
+
*/
|
|
51
|
+
shutdown: () => Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Left blank for downstream SDK modules to optionally implement.
|
|
54
|
+
*/
|
|
55
|
+
stop: () => void;
|
|
47
56
|
}
|
|
48
57
|
export {};
|
package/build/limiter.js
CHANGED
|
@@ -147,4 +147,15 @@ export class Limiter extends q {
|
|
|
147
147
|
return bound;
|
|
148
148
|
});
|
|
149
149
|
};
|
|
150
|
+
/**
|
|
151
|
+
* Implement any browserless-core-specific shutdown logic here.
|
|
152
|
+
* Calls the empty-SDK stop method for downstream implementations.
|
|
153
|
+
*/
|
|
154
|
+
shutdown = async () => {
|
|
155
|
+
await this.stop();
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Left blank for downstream SDK modules to optionally implement.
|
|
159
|
+
*/
|
|
160
|
+
stop = () => { };
|
|
150
161
|
}
|