@browserless.io/browserless 2.8.0 → 2.10.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 +13 -1
- package/README.md +41 -3
- package/assets/debugger.png +0 -0
- package/bin/scaffold/src/hello-world.http.ts +3 -2
- package/build/browserless.d.ts +5 -4
- package/build/browserless.js +19 -13
- package/build/browsers/chrome.cdp.d.ts +2 -2
- package/build/browsers/chrome.cdp.js +2 -2
- package/build/browsers/chrome.playwright.d.ts +2 -2
- package/build/browsers/chrome.playwright.js +2 -2
- package/build/browsers/chromium.cdp.d.ts +4 -4
- package/build/browsers/chromium.cdp.js +49 -32
- package/build/browsers/chromium.playwright.d.ts +4 -4
- package/build/browsers/chromium.playwright.js +14 -13
- package/build/browsers/firefox.playwright.d.ts +4 -4
- package/build/browsers/firefox.playwright.js +14 -13
- package/build/browsers/index.d.ts +24 -8
- package/build/browsers/index.js +20 -15
- package/build/browsers/webkit.playwright.d.ts +4 -4
- package/build/browsers/webkit.playwright.js +14 -13
- package/build/config.d.ts +3 -0
- package/build/config.js +3 -0
- package/build/file-system.d.ts +2 -3
- package/build/file-system.js +2 -2
- package/build/http.d.ts +1 -0
- package/build/http.js +1 -0
- package/build/index.js +7 -7
- package/build/limiter.d.ts +2 -3
- package/build/limiter.js +11 -11
- package/build/logger.d.ts +16 -9
- package/build/logger.js +32 -16
- package/build/monitoring.d.ts +2 -3
- package/build/monitoring.js +4 -4
- package/build/router.d.ts +1 -3
- package/build/router.js +21 -22
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/pdf.post.body.json +8 -8
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chromium/http/content.post.body.json +8 -8
- package/build/routes/chromium/http/pdf.post.body.json +8 -8
- package/build/routes/chromium/http/scrape.post.body.json +8 -8
- package/build/routes/chromium/http/screenshot.post.body.json +8 -8
- package/build/routes/management/http/pressure.get.d.ts +63 -0
- package/build/routes/management/http/pressure.get.js +56 -0
- package/build/routes/management/http/pressure.get.response.json +76 -0
- package/build/routes/management/http/static.get.js +3 -3
- package/build/routes/management/tests/management.spec.js +16 -0
- package/build/server.d.ts +1 -3
- package/build/server.js +33 -30
- package/build/shared/content.http.d.ts +1 -1
- package/build/shared/content.http.js +4 -1
- package/build/shared/download.http.js +9 -9
- package/build/shared/function.http.js +2 -2
- package/build/shared/json-protocol.http.d.ts +3 -3
- package/build/shared/json-protocol.http.js +2 -2
- package/build/shared/json-version.http.d.ts +3 -3
- package/build/shared/json-version.http.js +2 -2
- package/build/shared/pdf.http.d.ts +1 -1
- package/build/shared/pdf.http.js +6 -4
- package/build/shared/performance.http.js +1 -0
- package/build/shared/scrape.http.d.ts +1 -1
- package/build/shared/scrape.http.js +4 -1
- package/build/shared/screenshot.http.d.ts +1 -1
- package/build/shared/screenshot.http.js +4 -1
- package/build/shared/utils/function/handler.js +7 -7
- package/build/shared/utils/performance/child.js +4 -4
- package/build/shared/utils/performance/main.d.ts +1 -1
- package/build/shared/utils/performance/main.js +5 -7
- package/build/shared/utils/performance/types.d.ts +2 -1
- package/build/types.d.ts +10 -1
- package/build/types.js +10 -1
- package/build/utils.d.ts +1 -1
- package/build/utils.js +6 -2
- package/package.json +18 -15
- package/scripts/install-debugger.js +55 -15
- package/src/browserless.ts +25 -12
- package/src/browsers/chrome.cdp.ts +2 -5
- package/src/browsers/chrome.playwright.ts +2 -5
- package/src/browsers/chromium.cdp.ts +84 -35
- package/src/browsers/chromium.playwright.ts +26 -13
- package/src/browsers/firefox.playwright.ts +28 -13
- package/src/browsers/index.ts +24 -16
- package/src/browsers/webkit.playwright.ts +28 -13
- package/src/config.ts +4 -0
- package/src/file-system.ts +2 -7
- package/src/http.ts +1 -0
- package/src/index.ts +7 -7
- package/src/limiter.ts +13 -11
- package/src/logger.ts +39 -18
- package/src/monitoring.ts +6 -8
- package/src/router.ts +20 -20
- package/src/routes/management/http/pressure.get.ts +135 -0
- package/src/routes/management/http/static.get.ts +3 -5
- package/src/routes/management/tests/management.spec.ts +26 -0
- package/src/server.ts +43 -30
- package/src/shared/content.http.ts +5 -1
- package/src/shared/download.http.ts +9 -9
- package/src/shared/function.http.ts +2 -2
- package/src/shared/json-protocol.http.ts +8 -3
- package/src/shared/json-version.http.ts +8 -4
- package/src/shared/pdf.http.ts +6 -4
- package/src/shared/performance.http.ts +1 -0
- package/src/shared/scrape.http.ts +5 -1
- package/src/shared/screenshot.http.ts +4 -1
- package/src/shared/utils/function/handler.ts +7 -7
- package/src/shared/utils/performance/child.ts +4 -4
- package/src/shared/utils/performance/main.ts +5 -6
- package/src/shared/utils/performance/types.ts +2 -1
- package/src/types.ts +9 -0
- package/src/utils.ts +7 -2
- package/static/docs/swagger.json +138 -10
- package/static/docs/swagger.min.json +137 -9
- package/static/function/client.js +4290 -5542
- package/static/function/index.html +4290 -5542
|
@@ -24,11 +24,11 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute {
|
|
|
24
24
|
handler = async (req, res, logger, browser) => new Promise(async (resolve, reject) => {
|
|
25
25
|
const config = this.config();
|
|
26
26
|
const downloadPath = path.join(await config.getDownloadsDir(), `.browserless.download.${id()}`);
|
|
27
|
-
logger.
|
|
27
|
+
logger.info(`Generating a download directory at "${downloadPath}"`);
|
|
28
28
|
await mkdir(downloadPath);
|
|
29
29
|
const handler = functionHandler(config, logger, { downloadPath });
|
|
30
30
|
const response = await handler(req, browser).catch((e) => {
|
|
31
|
-
logger.
|
|
31
|
+
logger.error(`Error running download code handler: "${e}"`);
|
|
32
32
|
reject(e);
|
|
33
33
|
return null;
|
|
34
34
|
});
|
|
@@ -36,10 +36,10 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute {
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
const { page } = response;
|
|
39
|
-
logger.
|
|
39
|
+
logger.info(`Download function has returned, finding downloads...`);
|
|
40
40
|
async function checkIfDownloadComplete() {
|
|
41
41
|
if (res.headersSent) {
|
|
42
|
-
logger.
|
|
42
|
+
logger.trace(`Request headers have been sent, terminating download watch.`);
|
|
43
43
|
return null;
|
|
44
44
|
}
|
|
45
45
|
const [fileName] = await readdir(downloadPath);
|
|
@@ -47,20 +47,20 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute {
|
|
|
47
47
|
await sleep(500);
|
|
48
48
|
return checkIfDownloadComplete();
|
|
49
49
|
}
|
|
50
|
-
logger.
|
|
50
|
+
logger.info(`All files have finished downloading`);
|
|
51
51
|
return path.join(downloadPath, fileName);
|
|
52
52
|
}
|
|
53
53
|
const filePath = await checkIfDownloadComplete();
|
|
54
|
-
logger.
|
|
54
|
+
logger.info(`Closing pages.`);
|
|
55
55
|
page.close();
|
|
56
56
|
page.removeAllListeners();
|
|
57
57
|
const rmDownload = once(() => filePath &&
|
|
58
58
|
deleteAsync(filePath, { force: true })
|
|
59
59
|
.then(() => {
|
|
60
|
-
logger.
|
|
60
|
+
logger.info(`Successfully deleted downloads from disk at "${filePath}"`);
|
|
61
61
|
})
|
|
62
62
|
.catch((err) => {
|
|
63
|
-
logger.
|
|
63
|
+
logger.error(`Error cleaning up downloaded files: "${err}" at "${filePath}"`);
|
|
64
64
|
}));
|
|
65
65
|
if (res.headersSent || !filePath) {
|
|
66
66
|
rmDownload();
|
|
@@ -78,7 +78,7 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute {
|
|
|
78
78
|
}
|
|
79
79
|
})
|
|
80
80
|
.on('end', () => {
|
|
81
|
-
logger.
|
|
81
|
+
logger.info(`Downloads successfully sent`);
|
|
82
82
|
rmDownload();
|
|
83
83
|
return resolve();
|
|
84
84
|
})
|
|
@@ -22,7 +22,7 @@ export default class ChromiumFunctionPostRoute extends BrowserHTTPRoute {
|
|
|
22
22
|
const config = this.config();
|
|
23
23
|
const handler = functionHandler(config, logger);
|
|
24
24
|
const { contentType, payload, page } = await handler(req, browser);
|
|
25
|
-
logger.
|
|
25
|
+
logger.info(`Got function response of "${contentType}"`);
|
|
26
26
|
page.close();
|
|
27
27
|
page.removeAllListeners();
|
|
28
28
|
if (contentType === 'uint8array') {
|
|
@@ -33,7 +33,7 @@ export default class ChromiumFunctionPostRoute extends BrowserHTTPRoute {
|
|
|
33
33
|
throw new BadRequest(`Couldn't determine function's response type.`);
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
|
-
logger.
|
|
36
|
+
logger.info(`Sending file-type response of "${type}"`);
|
|
37
37
|
const readStream = new Stream.PassThrough();
|
|
38
38
|
readStream.end(response);
|
|
39
39
|
res.setHeader('Content-Type', type);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { APITags, HTTPRoute, HTTPRoutes, Methods, Request, Response, contentTypes } from '@browserless.io/browserless';
|
|
1
|
+
import { APITags, HTTPRoute, HTTPRoutes, Logger, Methods, Request, Response, contentTypes } from '@browserless.io/browserless';
|
|
2
2
|
export type ResponseSchema = object;
|
|
3
3
|
export default class ChromiumJSONProtocolGetRoute extends HTTPRoute {
|
|
4
|
-
|
|
4
|
+
protected cachedProtocol: object | undefined;
|
|
5
5
|
name: string;
|
|
6
6
|
accepts: contentTypes[];
|
|
7
7
|
auth: boolean;
|
|
@@ -12,5 +12,5 @@ export default class ChromiumJSONProtocolGetRoute extends HTTPRoute {
|
|
|
12
12
|
method: Methods;
|
|
13
13
|
path: HTTPRoutes;
|
|
14
14
|
tags: APITags[];
|
|
15
|
-
handler: (_req: Request, res: Response) => Promise<void>;
|
|
15
|
+
handler: (_req: Request, res: Response, logger: Logger) => Promise<void>;
|
|
16
16
|
}
|
|
@@ -11,10 +11,10 @@ export default class ChromiumJSONProtocolGetRoute extends HTTPRoute {
|
|
|
11
11
|
method = Methods.get;
|
|
12
12
|
path = HTTPRoutes.jsonProtocol;
|
|
13
13
|
tags = [APITags.browserAPI];
|
|
14
|
-
handler = async (_req, res) => {
|
|
14
|
+
handler = async (_req, res, logger) => {
|
|
15
15
|
const browserManager = this.browserManager();
|
|
16
16
|
if (!this.cachedProtocol) {
|
|
17
|
-
this.cachedProtocol = await browserManager.getProtocolJSON();
|
|
17
|
+
this.cachedProtocol = await browserManager.getProtocolJSON(logger);
|
|
18
18
|
}
|
|
19
19
|
return jsonResponse(res, 200, this.cachedProtocol);
|
|
20
20
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { APITags, BrowserManager, HTTPRoute, HTTPRoutes, Methods, Request, Response, UnwrapPromise, contentTypes } from '@browserless.io/browserless';
|
|
1
|
+
import { APITags, BrowserManager, HTTPRoute, HTTPRoutes, Logger, Methods, Request, Response, UnwrapPromise, contentTypes } from '@browserless.io/browserless';
|
|
2
2
|
export type ResponseSchema = UnwrapPromise<ReturnType<BrowserManager['getVersionJSON']>>;
|
|
3
3
|
export default class ChromiumJSONVersionGetRoute extends HTTPRoute {
|
|
4
|
-
|
|
4
|
+
protected cachedJSON: ResponseSchema | undefined;
|
|
5
5
|
name: string;
|
|
6
6
|
accepts: contentTypes[];
|
|
7
7
|
auth: boolean;
|
|
@@ -12,5 +12,5 @@ export default class ChromiumJSONVersionGetRoute extends HTTPRoute {
|
|
|
12
12
|
method: Methods;
|
|
13
13
|
path: HTTPRoutes;
|
|
14
14
|
tags: APITags[];
|
|
15
|
-
handler: (req: Request, res: Response) => Promise<void>;
|
|
15
|
+
handler: (req: Request, res: Response, logger: Logger) => Promise<void>;
|
|
16
16
|
}
|
|
@@ -11,14 +11,14 @@ export default class ChromiumJSONVersionGetRoute extends HTTPRoute {
|
|
|
11
11
|
method = Methods.get;
|
|
12
12
|
path = HTTPRoutes.jsonVersion;
|
|
13
13
|
tags = [APITags.browserAPI];
|
|
14
|
-
handler = async (req, res) => {
|
|
14
|
+
handler = async (req, res, logger) => {
|
|
15
15
|
const baseUrl = req.parsed.host;
|
|
16
16
|
const protocol = req.parsed.protocol.includes('s') ? 'wss' : 'ws';
|
|
17
17
|
try {
|
|
18
18
|
if (!this.cachedJSON) {
|
|
19
19
|
const browserManager = this.browserManager();
|
|
20
20
|
this.cachedJSON = {
|
|
21
|
-
...(await browserManager.getVersionJSON()),
|
|
21
|
+
...(await browserManager.getVersionJSON(logger)),
|
|
22
22
|
webSocketDebuggerUrl: `${protocol}://${baseUrl}`,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
@@ -43,5 +43,5 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
|
|
|
43
43
|
method: Methods;
|
|
44
44
|
path: HTTPRoutes[];
|
|
45
45
|
tags: APITags[];
|
|
46
|
-
handler: (req: Request, res: ServerResponse,
|
|
46
|
+
handler: (req: Request, res: ServerResponse, logger: Logger, browser: BrowserInstance) => Promise<void>;
|
|
47
47
|
}
|
package/build/shared/pdf.http.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { APITags, BadRequest, BrowserHTTPRoute, BrowserlessRoutes, ChromiumCDP, HTTPRoutes, Methods, bestAttemptCatch, contentTypes, dedent, noop, sleep, waitForEvent as waitForEvt, waitForFunction as waitForFn,
|
|
1
|
+
import { APITags, BadRequest, BrowserHTTPRoute, BrowserlessRoutes, ChromiumCDP, HTTPRoutes, Methods, bestAttemptCatch, contentTypes, dedent, noop, sleep, waitForEvent as waitForEvt, waitForFunction as waitForFn, } from '@browserless.io/browserless';
|
|
2
2
|
import { Stream } from 'stream';
|
|
3
3
|
export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
|
|
4
4
|
name = BrowserlessRoutes.ChromiumPDFPostRoute;
|
|
@@ -16,13 +16,13 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
|
|
|
16
16
|
method = Methods.post;
|
|
17
17
|
path = [HTTPRoutes.pdf, HTTPRoutes.chromiumPdf];
|
|
18
18
|
tags = [APITags.browserAPI];
|
|
19
|
-
handler = async (req, res,
|
|
19
|
+
handler = async (req, res, logger, browser) => {
|
|
20
|
+
logger.info('PDF API invoked with body:', req.body);
|
|
20
21
|
const contentType = !req.headers.accept || req.headers.accept?.includes('*')
|
|
21
22
|
? 'application/pdf'
|
|
22
23
|
: req.headers.accept;
|
|
23
24
|
if (!req.body) {
|
|
24
|
-
|
|
25
|
-
return;
|
|
25
|
+
throw new BadRequest(`Couldn't parse JSON body`);
|
|
26
26
|
}
|
|
27
27
|
res.setHeader('Content-Type', contentType);
|
|
28
28
|
const { url, gotoOptions, authenticate, html, addScriptTag = [], addStyleTag = [], cookies = [], emulateMediaType, rejectRequestPattern = [], requestInterceptors = [], rejectResourceTypes = [], options, setExtraHTTPHeaders, setJavaScriptEnabled, userAgent, viewport, waitForEvent, waitForFunction, waitForSelector, waitForTimeout, bestAttempt = false, } = req.body;
|
|
@@ -60,6 +60,7 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
|
|
|
60
60
|
page.on('request', (req) => {
|
|
61
61
|
if (!!rejectRequestPattern.find((pattern) => req.url().match(pattern)) ||
|
|
62
62
|
rejectResourceTypes.includes(req.resourceType())) {
|
|
63
|
+
logger.debug(`Aborting request ${req.method()}: ${req.url()}`);
|
|
63
64
|
return req.abort();
|
|
64
65
|
}
|
|
65
66
|
const interceptor = requestInterceptors.find((r) => req.url().match(r.pattern));
|
|
@@ -112,5 +113,6 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
|
|
|
112
113
|
readStream.end(pdfBuffer);
|
|
113
114
|
await new Promise((r) => readStream.pipe(res).once('close', r));
|
|
114
115
|
page.close().catch(noop);
|
|
116
|
+
logger.info('PDF API request completed');
|
|
115
117
|
};
|
|
116
118
|
}
|
|
@@ -115,5 +115,5 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute {
|
|
|
115
115
|
method: Methods;
|
|
116
116
|
path: HTTPRoutes[];
|
|
117
117
|
tags: APITags[];
|
|
118
|
-
handler: (req: Request, res: ServerResponse,
|
|
118
|
+
handler: (req: Request, res: ServerResponse, logger: Logger, browser: BrowserInstance) => Promise<void>;
|
|
119
119
|
}
|
|
@@ -54,7 +54,8 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute {
|
|
|
54
54
|
method = Methods.post;
|
|
55
55
|
path = [HTTPRoutes.scrape, HTTPRoutes.chromiumScrape];
|
|
56
56
|
tags = [APITags.browserAPI];
|
|
57
|
-
handler = async (req, res,
|
|
57
|
+
handler = async (req, res, logger, browser) => {
|
|
58
|
+
logger.info('Scrape API invoked with body:', req.body);
|
|
58
59
|
const contentType = !req.headers.accept || req.headers.accept?.includes('*')
|
|
59
60
|
? contentTypes.html
|
|
60
61
|
: req.headers.accept;
|
|
@@ -121,6 +122,7 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute {
|
|
|
121
122
|
page.on('request', (req) => {
|
|
122
123
|
if (!!rejectRequestPattern.find((pattern) => req.url().match(pattern)) ||
|
|
123
124
|
rejectResourceTypes.includes(req.resourceType())) {
|
|
125
|
+
logger.debug(`Aborting request ${req.method()}: ${req.url()}`);
|
|
124
126
|
return req.abort();
|
|
125
127
|
}
|
|
126
128
|
const interceptor = requestInterceptors.find((r) => req.url().match(r.pattern));
|
|
@@ -198,6 +200,7 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute {
|
|
|
198
200
|
debug: debugData,
|
|
199
201
|
};
|
|
200
202
|
page.close().catch(noop);
|
|
203
|
+
logger.info('Scrape API request completed');
|
|
201
204
|
return jsonResponse(res, 200, response, false);
|
|
202
205
|
};
|
|
203
206
|
}
|
|
@@ -46,5 +46,5 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
|
|
|
46
46
|
method: Methods;
|
|
47
47
|
path: HTTPRoutes[];
|
|
48
48
|
tags: APITags[];
|
|
49
|
-
handler: (req: Request, res: ServerResponse,
|
|
49
|
+
handler: (req: Request, res: ServerResponse, logger: Logger, browser: BrowserInstance) => Promise<void>;
|
|
50
50
|
}
|
|
@@ -15,7 +15,8 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
|
|
|
15
15
|
method = Methods.post;
|
|
16
16
|
path = [HTTPRoutes.screenshot, HTTPRoutes.chromiumScreenshot];
|
|
17
17
|
tags = [APITags.browserAPI];
|
|
18
|
-
handler = async (req, res,
|
|
18
|
+
handler = async (req, res, logger, browser) => {
|
|
19
|
+
logger.info('Screenshot API invoked with body:', req.body);
|
|
19
20
|
const contentType = !req.headers.accept || req.headers.accept?.includes('*')
|
|
20
21
|
? 'image/png'
|
|
21
22
|
: req.headers.accept;
|
|
@@ -61,6 +62,7 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
|
|
|
61
62
|
page.on('request', (req) => {
|
|
62
63
|
if (!!rejectRequestPattern.find((pattern) => req.url().match(pattern)) ||
|
|
63
64
|
rejectResourceTypes.includes(req.resourceType())) {
|
|
65
|
+
logger.debug(`Aborting request ${req.method()}: ${req.url()}`);
|
|
64
66
|
return req.abort();
|
|
65
67
|
}
|
|
66
68
|
const interceptor = requestInterceptors.find((r) => req.url().match(r.pattern));
|
|
@@ -122,5 +124,6 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
|
|
|
122
124
|
readStream.end(buffer);
|
|
123
125
|
await new Promise((r) => readStream.pipe(res).once('close', r));
|
|
124
126
|
page.close().catch(noop);
|
|
127
|
+
logger.info('Screenshot API request completed');
|
|
125
128
|
};
|
|
126
129
|
}
|
|
@@ -29,7 +29,7 @@ export default (config, logger, options = {}) => async (req, browser) => {
|
|
|
29
29
|
*/
|
|
30
30
|
page.on('request', async (request) => {
|
|
31
31
|
const requestUrl = request.url();
|
|
32
|
-
logger.
|
|
32
|
+
logger.info(`Outbound Page Request: "${requestUrl}"`);
|
|
33
33
|
if (requestUrl.startsWith(functionRequestPath)) {
|
|
34
34
|
const filename = path.basename(requestUrl);
|
|
35
35
|
if (filename === functionCodeJS) {
|
|
@@ -48,23 +48,23 @@ export default (config, logger, options = {}) => async (req, browser) => {
|
|
|
48
48
|
status: 200,
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
-
logger.
|
|
51
|
+
logger.warn(`Static asset request to "${requestUrl}" couldn't be found, 404-ing`);
|
|
52
52
|
return request.respond({
|
|
53
53
|
body: code,
|
|
54
54
|
contentType: `Couldn't locate this file "${filename}" request "${requestUrl}" in "${functionAssetLocation}"`,
|
|
55
55
|
status: 404,
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
|
-
logger.
|
|
58
|
+
logger.info(`Request: "${requestUrl}" no responder found, continuing...`);
|
|
59
59
|
return request.continue();
|
|
60
60
|
});
|
|
61
61
|
page.on('response', (res) => {
|
|
62
|
-
if (res.
|
|
63
|
-
logger.
|
|
62
|
+
if (!res.ok()) {
|
|
63
|
+
logger.warn(`Received a non-200 response for request "${res.url()}"`);
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
page.on('console', (event) => {
|
|
67
|
-
logger.
|
|
67
|
+
logger.trace(`${event.type()}: ${event.text()}`);
|
|
68
68
|
});
|
|
69
69
|
await page.goto(functionIndexHTML);
|
|
70
70
|
const { contentType, payload } = await page
|
|
@@ -85,7 +85,7 @@ export default (config, logger, options = {}) => async (req, browser) => {
|
|
|
85
85
|
});
|
|
86
86
|
}, browserWSEndpoint, context, functionCodeJS, JSON.stringify(options))
|
|
87
87
|
.catch((e) => {
|
|
88
|
-
logger.
|
|
88
|
+
logger.error(`Error running code: ${e}`);
|
|
89
89
|
throw new BadRequest(e.message);
|
|
90
90
|
});
|
|
91
91
|
return {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Logger } from '@browserless.io/browserless';
|
|
2
2
|
import lighthouse from 'lighthouse';
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const logger = new Logger('http:performance:child');
|
|
4
|
+
logger.info(`Child init`);
|
|
5
5
|
const send = (msg) => process.send && process.send(msg);
|
|
6
6
|
const start = async ({ url, config, options }) => {
|
|
7
7
|
try {
|
|
8
|
-
|
|
8
|
+
logger.info(`Child got payload, starting lighthouse`);
|
|
9
9
|
const results = await lighthouse(url, options, config);
|
|
10
10
|
send({
|
|
11
11
|
data: results?.lhr,
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import { createLogger } from '@browserless.io/browserless';
|
|
2
1
|
import { fork } from 'child_process';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
const DEFAULT_AUDIT_CONFIG = {
|
|
5
4
|
extends: 'lighthouse:default',
|
|
6
5
|
};
|
|
7
|
-
export default async ({ browser, context, timeout, }) => {
|
|
6
|
+
export default async ({ browser, context, logger, timeout, }) => {
|
|
8
7
|
return new Promise((resolve, reject) => {
|
|
9
|
-
const debug = createLogger('http:performance:main');
|
|
10
8
|
const childPath = path.join('./', 'build', 'shared', 'utils', 'performance', 'child.js');
|
|
11
|
-
|
|
9
|
+
logger.trace(`Starting up child at ${childPath}`);
|
|
12
10
|
const child = fork(childPath);
|
|
13
11
|
const port = new URL(browser.wsEndpoint() || '').port;
|
|
14
12
|
let closed = false;
|
|
@@ -34,13 +32,13 @@ export default async ({ browser, context, timeout, }) => {
|
|
|
34
32
|
port,
|
|
35
33
|
};
|
|
36
34
|
child.on('error', (err) => {
|
|
37
|
-
|
|
35
|
+
logger.error(`Error in child process`, err);
|
|
38
36
|
reject('Performance run error: ' + err.message);
|
|
39
37
|
close(child.pid);
|
|
40
38
|
});
|
|
41
39
|
child.on('message', (payload) => {
|
|
42
40
|
if (payload.event === 'created') {
|
|
43
|
-
|
|
41
|
+
logger.info(`Child process is up, sending performance request`);
|
|
44
42
|
return child.send({
|
|
45
43
|
config,
|
|
46
44
|
event: 'start',
|
|
@@ -49,7 +47,7 @@ export default async ({ browser, context, timeout, }) => {
|
|
|
49
47
|
});
|
|
50
48
|
}
|
|
51
49
|
if (payload.event === 'complete') {
|
|
52
|
-
|
|
50
|
+
logger.info(`Performance gathered, closing and resolving request`);
|
|
53
51
|
close(child.pid);
|
|
54
52
|
return resolve({
|
|
55
53
|
data: payload.data,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { BrowserInstance, Logger } from '@browserless.io/browserless';
|
|
1
2
|
import { Config, Flags } from 'lighthouse';
|
|
2
|
-
import { BrowserInstance } from '@browserless.io/browserless';
|
|
3
3
|
export interface Message {
|
|
4
4
|
data?: unknown;
|
|
5
5
|
error?: unknown;
|
|
@@ -12,6 +12,7 @@ export interface mainOptions {
|
|
|
12
12
|
config?: unknown;
|
|
13
13
|
url: string;
|
|
14
14
|
};
|
|
15
|
+
logger: Logger;
|
|
15
16
|
timeout: number;
|
|
16
17
|
}
|
|
17
18
|
export interface start {
|
package/build/types.d.ts
CHANGED
|
@@ -54,7 +54,8 @@ declare abstract class Route {
|
|
|
54
54
|
protected _metrics: Browserless['metrics'];
|
|
55
55
|
protected _monitoring: Browserless['monitoring'];
|
|
56
56
|
protected _staticSDKDir: Browserless['staticSDKDir'];
|
|
57
|
-
|
|
57
|
+
protected _limiter: Browserless['limiter'];
|
|
58
|
+
constructor(_browserManager: Browserless['browserManager'], _config: Browserless['config'], _fileSystem: Browserless['fileSystem'], _metrics: Browserless['metrics'], _monitoring: Browserless['monitoring'], _staticSDKDir: Browserless['staticSDKDir'], _limiter: Browserless['limiter']);
|
|
58
59
|
/**
|
|
59
60
|
* A unique name to identify this route. Used in downstream
|
|
60
61
|
* SDKs to potentially remove or disable.
|
|
@@ -133,6 +134,12 @@ declare abstract class Route {
|
|
|
133
134
|
* @returns {string | null} The full path location of the SDK's static directory
|
|
134
135
|
*/
|
|
135
136
|
staticSDKDir: () => string | null;
|
|
137
|
+
/**
|
|
138
|
+
* Helper function that loads the limiter module into the router's
|
|
139
|
+
* handler scope.
|
|
140
|
+
* @returns Limiter
|
|
141
|
+
*/
|
|
142
|
+
limiter: () => import("@browserless.io/browserless").Limiter;
|
|
136
143
|
/**
|
|
137
144
|
* The HTTP path that this route handles, eg '/my-route' OR an
|
|
138
145
|
* array of paths that this route can handle.
|
|
@@ -510,6 +517,7 @@ export declare const BrowserlessManagementRoutes: {
|
|
|
510
517
|
ConfigGetRoute: string;
|
|
511
518
|
MetricsGetRoute: string;
|
|
512
519
|
MetricsTotalGetRoute: string;
|
|
520
|
+
PressureGetRoute: string;
|
|
513
521
|
SessionsGetRoute: string;
|
|
514
522
|
StaticGetRoute: string;
|
|
515
523
|
};
|
|
@@ -518,6 +526,7 @@ export declare const BrowserlessRoutes: {
|
|
|
518
526
|
ConfigGetRoute: string;
|
|
519
527
|
MetricsGetRoute: string;
|
|
520
528
|
MetricsTotalGetRoute: string;
|
|
529
|
+
PressureGetRoute: string;
|
|
521
530
|
SessionsGetRoute: string;
|
|
522
531
|
StaticGetRoute: string;
|
|
523
532
|
WebKitPlaywrightWebSocketRoute: string;
|
package/build/types.js
CHANGED
|
@@ -5,13 +5,15 @@ class Route {
|
|
|
5
5
|
_metrics;
|
|
6
6
|
_monitoring;
|
|
7
7
|
_staticSDKDir;
|
|
8
|
-
|
|
8
|
+
_limiter;
|
|
9
|
+
constructor(_browserManager, _config, _fileSystem, _metrics, _monitoring, _staticSDKDir, _limiter) {
|
|
9
10
|
this._browserManager = _browserManager;
|
|
10
11
|
this._config = _config;
|
|
11
12
|
this._fileSystem = _fileSystem;
|
|
12
13
|
this._metrics = _metrics;
|
|
13
14
|
this._monitoring = _monitoring;
|
|
14
15
|
this._staticSDKDir = _staticSDKDir;
|
|
16
|
+
this._limiter = _limiter;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* A boolean, or a function that returns a boolean, on
|
|
@@ -86,6 +88,12 @@ class Route {
|
|
|
86
88
|
* @returns {string | null} The full path location of the SDK's static directory
|
|
87
89
|
*/
|
|
88
90
|
staticSDKDir = () => this._staticSDKDir;
|
|
91
|
+
/**
|
|
92
|
+
* Helper function that loads the limiter module into the router's
|
|
93
|
+
* handler scope.
|
|
94
|
+
* @returns Limiter
|
|
95
|
+
*/
|
|
96
|
+
limiter = () => this._limiter;
|
|
89
97
|
}
|
|
90
98
|
/**
|
|
91
99
|
* A primitive HTTP-based route that doesn't require a
|
|
@@ -184,6 +192,7 @@ export const BrowserlessManagementRoutes = {
|
|
|
184
192
|
ConfigGetRoute: 'ConfigGetRoute',
|
|
185
193
|
MetricsGetRoute: 'MetricsGetRoute',
|
|
186
194
|
MetricsTotalGetRoute: 'MetricsTotalGetRoute',
|
|
195
|
+
PressureGetRoute: 'PressureGetRoute',
|
|
187
196
|
SessionsGetRoute: 'SessionsGetRoute',
|
|
188
197
|
StaticGetRoute: 'StaticGetRoute',
|
|
189
198
|
};
|
package/build/utils.d.ts
CHANGED
|
@@ -120,6 +120,6 @@ interface RequestInitTimeout extends RequestInit {
|
|
|
120
120
|
}
|
|
121
121
|
export declare const fetchTimeout: (input: RequestInfo | URL, initWithTimeout?: RequestInitTimeout) => Promise<Response>;
|
|
122
122
|
export declare const untildify: (path: string) => string;
|
|
123
|
-
export declare const printLogo: (docsLink: string) => string;
|
|
123
|
+
export declare const printLogo: (docsLink: string, debugURL?: string | boolean) => string;
|
|
124
124
|
export declare const getCDPClient: (page: Page) => CDPSession;
|
|
125
125
|
export {};
|
package/build/utils.js
CHANGED
|
@@ -561,12 +561,16 @@ export const untildify = (path) => {
|
|
|
561
561
|
const homeDir = homedir();
|
|
562
562
|
return homeDir ? path.replace(/^~(?=$|\/|\\)/, homeDir) : path;
|
|
563
563
|
};
|
|
564
|
-
export const printLogo = (docsLink) => `
|
|
564
|
+
export const printLogo = (docsLink, debugURL) => `
|
|
565
565
|
---------------------------------------------------------
|
|
566
566
|
| browserless.io
|
|
567
567
|
| To read documentation and more, load in your browser:
|
|
568
568
|
|
|
|
569
|
-
| ${docsLink}
|
|
569
|
+
| OpenAPI: ${docsLink}
|
|
570
|
+
| Full Documentation: https://docs.browserless.io/ ${
|
|
571
|
+
/*prettier-ignore*/
|
|
572
|
+
debugURL ? `
|
|
573
|
+
| Debbuger: ${debugURL}` : ""}
|
|
570
574
|
---------------------------------------------------------
|
|
571
575
|
${gradient('#ff1a8c', '#ffea00')(`
|
|
572
576
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserless.io/browserless",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"license": "SSPL",
|
|
5
5
|
"description": "The browserless platform",
|
|
6
6
|
"author": "browserless.io",
|
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
"clean": "node scripts/clean.js",
|
|
26
26
|
"dev": "npm run build:dev && env-cmd -f .env node build",
|
|
27
27
|
"install:adblock": "node scripts/install-adblock.js",
|
|
28
|
+
"install:debugger": "node scripts/install-debugger.js",
|
|
28
29
|
"install:browsers": "npx --yes playwright install chromium firefox webkit chrome",
|
|
29
|
-
"install:dev": "npm run install:browsers",
|
|
30
|
+
"install:dev": "npm run install:browsers && npm run install:debugger",
|
|
30
31
|
"lint": "eslint . --ext .ts --fix",
|
|
31
32
|
"prepack": "npm run build:dev",
|
|
32
33
|
"prettier": "prettier '{src,functions,scripts,bin,external,.github}/**/*.{js,ts,json,yml,yaml,md}' --log-level error --write",
|
|
@@ -55,40 +56,42 @@
|
|
|
55
56
|
"get-port": "^7.1.0",
|
|
56
57
|
"gradient-string": "^2.0.0",
|
|
57
58
|
"http-proxy": "^1.18.1",
|
|
58
|
-
"lighthouse": "^
|
|
59
|
+
"lighthouse": "^12.0.0",
|
|
59
60
|
"micromatch": "^4.0.4",
|
|
60
|
-
"playwright-core": "1.
|
|
61
|
-
"puppeteer-core": "^22.
|
|
61
|
+
"playwright-core": "1.44.0",
|
|
62
|
+
"puppeteer-core": "^22.8.1",
|
|
62
63
|
"puppeteer-extra": "^3.3.6",
|
|
63
64
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
64
65
|
"queue": "^7.0.0",
|
|
65
|
-
"systeminformation": "^5.22.
|
|
66
|
+
"systeminformation": "^5.22.9",
|
|
67
|
+
"tar-fs": "^3.0.6"
|
|
66
68
|
},
|
|
67
69
|
"optionalDependencies": {
|
|
68
|
-
"@types/chai": "^4.3.
|
|
70
|
+
"@types/chai": "^4.3.16",
|
|
69
71
|
"@types/debug": "^4.1.12",
|
|
70
72
|
"@types/gradient-string": "^1.1.6",
|
|
71
73
|
"@types/http-proxy": "^1.17.14",
|
|
72
|
-
"@types/micromatch": "^4.0.
|
|
74
|
+
"@types/micromatch": "^4.0.7",
|
|
73
75
|
"@types/mocha": "^10.0.6",
|
|
74
|
-
"@types/node": "^20.12.
|
|
76
|
+
"@types/node": "^20.12.12",
|
|
75
77
|
"@types/sinon": "^17.0.3",
|
|
76
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
77
|
-
"@typescript-eslint/parser": "^7.
|
|
78
|
+
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
|
79
|
+
"@typescript-eslint/parser": "^7.9.0",
|
|
78
80
|
"assert": "^2.0.0",
|
|
79
|
-
"chai": "^5.1.
|
|
81
|
+
"chai": "^5.1.1",
|
|
80
82
|
"cross-env": "^7.0.3",
|
|
81
83
|
"env-cmd": "^10.1.0",
|
|
82
|
-
"esbuild": "^0.
|
|
84
|
+
"esbuild": "^0.21.2",
|
|
83
85
|
"esbuild-plugin-polyfill-node": "^0.3.0",
|
|
84
86
|
"eslint": "^8.57.0",
|
|
85
87
|
"eslint-plugin-typescript-sort-keys": "^3.2.0",
|
|
86
88
|
"extract-zip": "^2.0.1",
|
|
87
|
-
"
|
|
89
|
+
"gunzip-maybe": "^1.4.2",
|
|
90
|
+
"marked": "^12.0.2",
|
|
88
91
|
"mocha": "^10.4.0",
|
|
89
92
|
"move-file": "^3.1.0",
|
|
90
93
|
"prettier": "^3.2.5",
|
|
91
|
-
"sinon": "^17.0.
|
|
94
|
+
"sinon": "^17.0.2",
|
|
92
95
|
"ts-node": "^10.9.2",
|
|
93
96
|
"typescript": "^5.4.5",
|
|
94
97
|
"typescript-json-schema": "^0.63.0"
|