@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.
- package/CHANGELOG.md +6 -0
- package/bin/browserless.js +2 -0
- package/bin/scaffold/README.md +26 -1
- package/build/browserless.d.ts +6 -2
- package/build/browserless.js +23 -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/data/selectors.json +1 -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 -8
- 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/tests/function.spec.js +32 -0
- 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 -8
- 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/management/http/static.get.js +17 -11
- 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/token.d.ts +12 -1
- package/build/token.js +14 -1
- package/build/types.d.ts +9 -12
- package/build/types.js +10 -1
- package/build/webhooks.d.ts +12 -1
- package/build/webhooks.js +14 -1
- package/extensions/.gitkeep +0 -0
- package/package.json +5 -5
- package/src/browserless.ts +24 -2
- 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/routes/chrome/tests/function.spec.ts +35 -0
- package/src/routes/management/http/static.get.ts +25 -15
- package/src/server.ts +18 -16
- package/src/token.ts +18 -2
- package/src/types.ts +9 -13
- package/src/webhooks.ts +18 -2
- package/static/docs/swagger.json +10 -186
- package/static/docs/swagger.min.json +9 -185
- package/extensions/screencast/background.js +0 -143
- package/extensions/screencast/content_script.js +0 -18
- package/extensions/screencast/manifest.json +0 -19
package/build/types.js
CHANGED
|
@@ -5,13 +5,15 @@ class Route {
|
|
|
5
5
|
_debug;
|
|
6
6
|
_metrics;
|
|
7
7
|
_monitoring;
|
|
8
|
-
|
|
8
|
+
_staticSDKDir;
|
|
9
|
+
constructor(_browserManager, _config, _fileSystem, _debug, _metrics, _monitoring, _staticSDKDir) {
|
|
9
10
|
this._browserManager = _browserManager;
|
|
10
11
|
this._config = _config;
|
|
11
12
|
this._fileSystem = _fileSystem;
|
|
12
13
|
this._debug = _debug;
|
|
13
14
|
this._metrics = _metrics;
|
|
14
15
|
this._monitoring = _monitoring;
|
|
16
|
+
this._staticSDKDir = _staticSDKDir;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* A boolean, or a function that returns a boolean, on
|
|
@@ -86,6 +88,13 @@ class Route {
|
|
|
86
88
|
* @returns Monitor
|
|
87
89
|
*/
|
|
88
90
|
monitoring = () => this._monitoring;
|
|
91
|
+
/**
|
|
92
|
+
* When running in an SDK environment, this returns the fully-qualified
|
|
93
|
+
* directory of that static directory. When "null" then no SDK directory
|
|
94
|
+
* has been set.
|
|
95
|
+
* @returns {string | null} The full path location of the SDK's static directory
|
|
96
|
+
*/
|
|
97
|
+
staticSDKDir = () => this._staticSDKDir;
|
|
89
98
|
}
|
|
90
99
|
/**
|
|
91
100
|
* A primitive HTTP-based route that doesn't require a
|
package/build/webhooks.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { Config } from '@browserless.io/browserless';
|
|
2
|
-
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
export declare class WebHooks extends EventEmitter {
|
|
3
5
|
protected config: Config;
|
|
4
6
|
constructor(config: Config);
|
|
5
7
|
protected callURL(url: string | null): Promise<void | Response> | undefined;
|
|
@@ -8,4 +10,13 @@ export declare class WebHooks {
|
|
|
8
10
|
callRejectAlertURL(): Promise<void | Response> | undefined;
|
|
9
11
|
callTimeoutAlertURL(): Promise<void | Response> | undefined;
|
|
10
12
|
callErrorAlertURL(message: string): void | Promise<void | Response>;
|
|
13
|
+
/**
|
|
14
|
+
* Implement any browserless-core-specific shutdown logic here.
|
|
15
|
+
* Calls the empty-SDK stop method for downstream implementations.
|
|
16
|
+
*/
|
|
17
|
+
shutdown: () => Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Left blank for downstream SDK modules to optionally implement.
|
|
20
|
+
*/
|
|
21
|
+
stop: () => void;
|
|
11
22
|
}
|
package/build/webhooks.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { fetchTimeout, noop } from '@browserless.io/browserless';
|
|
2
|
-
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
export class WebHooks extends EventEmitter {
|
|
3
4
|
config;
|
|
4
5
|
constructor(config) {
|
|
6
|
+
super();
|
|
5
7
|
this.config = config;
|
|
6
8
|
}
|
|
7
9
|
callURL(url) {
|
|
@@ -40,4 +42,15 @@ export class WebHooks {
|
|
|
40
42
|
return console.error(`Issue calling error hook: "${err}". Did you set a working ERROR_ALERT_URL env variable?`);
|
|
41
43
|
}
|
|
42
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Implement any browserless-core-specific shutdown logic here.
|
|
47
|
+
* Calls the empty-SDK stop method for downstream implementations.
|
|
48
|
+
*/
|
|
49
|
+
shutdown = async () => {
|
|
50
|
+
await this.stop();
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Left blank for downstream SDK modules to optionally implement.
|
|
54
|
+
*/
|
|
55
|
+
stop = () => { };
|
|
43
56
|
}
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserless.io/browserless",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0-beta-3",
|
|
4
4
|
"license": "SSPL",
|
|
5
5
|
"description": "The browserless platform",
|
|
6
6
|
"author": "browserless.io",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"http-proxy": "^1.18.1",
|
|
58
58
|
"lighthouse": "^11.1.0",
|
|
59
59
|
"micromatch": "^4.0.4",
|
|
60
|
-
"playwright-core": "^1.42.
|
|
60
|
+
"playwright-core": "^1.42.1",
|
|
61
61
|
"puppeteer-core": "^22.3.0",
|
|
62
62
|
"puppeteer-extra": "^3.3.6",
|
|
63
63
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"@types/http-proxy": "^1.17.14",
|
|
72
72
|
"@types/micromatch": "^4.0.6",
|
|
73
73
|
"@types/mocha": "^10.0.6",
|
|
74
|
-
"@types/node": "^20.11.
|
|
74
|
+
"@types/node": "^20.11.24",
|
|
75
75
|
"@types/sinon": "^17.0.3",
|
|
76
|
-
"@typescript-eslint/eslint-plugin": "^7.1.
|
|
77
|
-
"@typescript-eslint/parser": "^7.1.
|
|
76
|
+
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
|
77
|
+
"@typescript-eslint/parser": "^7.1.1",
|
|
78
78
|
"assert": "^2.0.0",
|
|
79
79
|
"chai": "^5.1.0",
|
|
80
80
|
"cross-env": "^7.0.3",
|
package/src/browserless.ts
CHANGED
|
@@ -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;
|
|
@@ -53,6 +54,7 @@ export class Browserless {
|
|
|
53
54
|
protected router: Router;
|
|
54
55
|
protected token: Token;
|
|
55
56
|
protected webhooks: WebHooks;
|
|
57
|
+
protected staticSDKDir: string | null = null;
|
|
56
58
|
|
|
57
59
|
disabledRouteNames: string[] = [];
|
|
58
60
|
webSocketRouteFiles: string[] = [];
|
|
@@ -82,6 +84,7 @@ export class Browserless {
|
|
|
82
84
|
token?: Browserless['token'];
|
|
83
85
|
webhooks?: Browserless['webhooks'];
|
|
84
86
|
} = {}) {
|
|
87
|
+
super();
|
|
85
88
|
this.config = config || new Config();
|
|
86
89
|
this.metrics = metrics || new Metrics();
|
|
87
90
|
this.token = token || new Token(this.config);
|
|
@@ -149,6 +152,10 @@ export class Browserless {
|
|
|
149
152
|
return this.disabledRouteNames.some((name) => name === route.name);
|
|
150
153
|
}
|
|
151
154
|
|
|
155
|
+
public setStaticSDKDir(dir: string) {
|
|
156
|
+
this.staticSDKDir = dir;
|
|
157
|
+
}
|
|
158
|
+
|
|
152
159
|
public disableRoutes(...routeNames: string[]) {
|
|
153
160
|
this.disabledRouteNames.push(...routeNames);
|
|
154
161
|
}
|
|
@@ -172,7 +179,18 @@ export class Browserless {
|
|
|
172
179
|
|
|
173
180
|
public async stop() {
|
|
174
181
|
clearInterval(this.metricsSaveIntervalID as unknown as number);
|
|
175
|
-
return Promise.all([
|
|
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
|
+
]);
|
|
176
194
|
}
|
|
177
195
|
|
|
178
196
|
public async start() {
|
|
@@ -226,6 +244,7 @@ export class Browserless {
|
|
|
226
244
|
logger,
|
|
227
245
|
this.metrics,
|
|
228
246
|
this.monitoring,
|
|
247
|
+
this.staticSDKDir,
|
|
229
248
|
);
|
|
230
249
|
|
|
231
250
|
if (!this.routeIsDisabled(route)) {
|
|
@@ -236,6 +255,7 @@ export class Browserless {
|
|
|
236
255
|
route.monitoring = () => this.monitoring;
|
|
237
256
|
route.fileSystem = () => this.fileSystem;
|
|
238
257
|
route.debug = () => logger;
|
|
258
|
+
route.staticSDKDir = () => this.staticSDKDir;
|
|
239
259
|
|
|
240
260
|
httpRoutes.push(route);
|
|
241
261
|
}
|
|
@@ -277,6 +297,7 @@ export class Browserless {
|
|
|
277
297
|
logger,
|
|
278
298
|
this.metrics,
|
|
279
299
|
this.monitoring,
|
|
300
|
+
this.staticSDKDir,
|
|
280
301
|
);
|
|
281
302
|
|
|
282
303
|
if (!this.routeIsDisabled(route)) {
|
|
@@ -286,6 +307,7 @@ export class Browserless {
|
|
|
286
307
|
route.monitoring = () => this.monitoring;
|
|
287
308
|
route.fileSystem = () => this.fileSystem;
|
|
288
309
|
route.debug = () => logger;
|
|
310
|
+
route.staticSDKDir = () => this.staticSDKDir;
|
|
289
311
|
|
|
290
312
|
wsRoutes.push(route);
|
|
291
313
|
}
|
|
@@ -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.
|
|
272
|
-
const requiredExtensionArgs: string[] = [];
|
|
138
|
+
if (this.blockAds) {
|
|
273
139
|
// Necessary to load extensions
|
|
274
140
|
finalOptions.headless = false;
|
|
275
141
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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({
|
package/src/browsers/index.ts
CHANGED
|
@@ -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
|
|
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
package/src/file-system.ts
CHANGED
|
@@ -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.
|