@browserless.io/browserless 2.18.0 → 2.20.0-beta-1
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 +8 -1
- package/README.md +2 -2
- package/bin/browserless.js +2 -2
- package/build/browserless.js +4 -3
- package/build/browsers/index.js +6 -2
- package/build/config.d.ts +1 -1
- package/build/config.js +2 -2
- package/build/limiter.spec.js +1 -0
- 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/chrome/tests/content.spec.js +1 -0
- package/build/routes/chrome/ws/page.d.ts +1 -0
- package/build/routes/chrome/ws/page.js +1 -0
- 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/sdk-utils.js +18 -46
- package/build/server.js +3 -1
- package/build/shared/json-version.http.js +1 -0
- package/build/shared/utils/performance/main.js +2 -1
- package/build/utils.d.ts +8 -0
- package/build/utils.js +15 -0
- package/docker/chrome/Dockerfile +3 -2
- package/docker/chromium/Dockerfile +3 -2
- package/docker/firefox/Dockerfile +3 -2
- package/docker/multi/Dockerfile +4 -2
- package/docker/webkit/Dockerfile +3 -2
- package/package.json +20 -30
- package/src/browserless.ts +5 -4
- package/src/browsers/index.ts +6 -2
- package/src/config.ts +2 -2
- package/src/limiter.spec.ts +1 -0
- package/src/routes/chrome/tests/content.spec.ts +1 -0
- package/src/routes/chrome/ws/page.ts +1 -0
- package/src/sdk-utils.ts +32 -60
- package/src/server.ts +4 -1
- package/src/shared/json-version.http.ts +1 -0
- package/src/shared/utils/performance/main.ts +1 -1
- package/src/utils.ts +17 -0
- package/static/docs/swagger.json +10 -10
- package/static/docs/swagger.min.json +9 -9
- package/static/function/client.js +14 -8
- package/static/function/index.html +14 -8
package/build/sdk-utils.js
CHANGED
|
@@ -2,7 +2,15 @@ import { createInterface } from 'readline';
|
|
|
2
2
|
import debug from 'debug';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import {
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { exec } from 'child_process';
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
const waitForCommand = async (cmd, workingDirectory) => new Promise((resolve, reject) => execAsync(cmd, { cwd: workingDirectory }).then(({ stderr }) => {
|
|
9
|
+
if (stderr) {
|
|
10
|
+
return reject(`Error running ${cmd}. See output for more details: \n${stderr}`);
|
|
11
|
+
}
|
|
12
|
+
return resolve();
|
|
13
|
+
}));
|
|
6
14
|
export const getArgSwitches = () => {
|
|
7
15
|
return process.argv.reduce((accum, arg, idx) => {
|
|
8
16
|
if (!arg.startsWith('--')) {
|
|
@@ -28,7 +36,11 @@ export const getSourceFiles = async (cwd) => {
|
|
|
28
36
|
const buildDir = path.join(cwd, 'build');
|
|
29
37
|
const files = await fs.readdir(buildDir, { recursive: true });
|
|
30
38
|
const [httpRoutes, webSocketRoutes] = files.reduce(([httpRoutes, webSocketRoutes], file) => {
|
|
39
|
+
const isInRootDir = !file.includes(path.sep);
|
|
31
40
|
const parsed = path.parse(file);
|
|
41
|
+
if (isInRootDir) {
|
|
42
|
+
return [httpRoutes, webSocketRoutes];
|
|
43
|
+
}
|
|
32
44
|
if (parsed.name.endsWith('http')) {
|
|
33
45
|
httpRoutes.push(path.join(buildDir, file));
|
|
34
46
|
}
|
|
@@ -58,50 +70,10 @@ export const prompt = async (question) => {
|
|
|
58
70
|
});
|
|
59
71
|
});
|
|
60
72
|
};
|
|
73
|
+
// Exceptions are not caught, since any error would result in a crash regardless
|
|
61
74
|
export const installDependencies = async (workingDirectory) => {
|
|
62
|
-
await
|
|
63
|
-
|
|
64
|
-
cwd: workingDirectory,
|
|
65
|
-
stdio: 'inherit',
|
|
66
|
-
}).once('close', (code) => {
|
|
67
|
-
if (code === 0) {
|
|
68
|
-
return resolve();
|
|
69
|
-
}
|
|
70
|
-
return reject(`Error when installing dependencies, see output for more details`);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
await new Promise((resolve, reject) => {
|
|
74
|
-
spawn('npx', 'playwright-core install --with-deps chromium firefox webkit'.split(' '), {
|
|
75
|
-
cwd: workingDirectory,
|
|
76
|
-
stdio: 'inherit',
|
|
77
|
-
}).once('close', (code) => {
|
|
78
|
-
if (code === 0) {
|
|
79
|
-
return resolve();
|
|
80
|
-
}
|
|
81
|
-
return reject(`Error when installing dependencies, see output for more details`);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
75
|
+
await waitForCommand('npm install', workingDirectory);
|
|
76
|
+
await waitForCommand('npx playwright-core install --with-deps chromium firefox webkit', workingDirectory);
|
|
84
77
|
};
|
|
85
|
-
export const buildDockerImage = async (cmd, projectDir) =>
|
|
86
|
-
|
|
87
|
-
spawn(docker, args, {
|
|
88
|
-
cwd: projectDir,
|
|
89
|
-
stdio: 'inherit',
|
|
90
|
-
}).once('close', (code) => {
|
|
91
|
-
if (code === 0) {
|
|
92
|
-
return resolve();
|
|
93
|
-
}
|
|
94
|
-
return reject(`Error when building Docker image, see output for more details`);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
export const buildTypeScript = async (buildDir, projectDir) => new Promise((resolve, reject) => {
|
|
98
|
-
spawn('npx', ['tsc', '--outDir', buildDir], {
|
|
99
|
-
cwd: projectDir,
|
|
100
|
-
stdio: 'inherit',
|
|
101
|
-
}).once('close', (code) => {
|
|
102
|
-
if (code === 0) {
|
|
103
|
-
return resolve();
|
|
104
|
-
}
|
|
105
|
-
return reject(`Error in building TypeScript, see output for more details`);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
78
|
+
export const buildDockerImage = async (cmd, projectDir) => waitForCommand(cmd, projectDir);
|
|
79
|
+
export const buildTypeScript = async (buildDir, projectDir) => waitForCommand(`npx tsc --outDir ${buildDir}`, projectDir);
|
package/build/server.js
CHANGED
|
@@ -241,7 +241,9 @@ export class HTTPServer extends EventEmitter {
|
|
|
241
241
|
async shutdown() {
|
|
242
242
|
this.logger.info(`HTTP Server is shutting down`);
|
|
243
243
|
await new Promise((r) => this.server.close(r));
|
|
244
|
-
|
|
244
|
+
if (this.server) {
|
|
245
|
+
this.server.removeAllListeners();
|
|
246
|
+
}
|
|
245
247
|
// @ts-ignore garbage collect this reference
|
|
246
248
|
this.server = null;
|
|
247
249
|
this.logger.info(`HTTP Server shutdown complete`);
|
|
@@ -25,6 +25,7 @@ export default class ChromiumJSONVersionGetRoute extends HTTPRoute {
|
|
|
25
25
|
return jsonResponse(res, 200, this.cachedJSON);
|
|
26
26
|
}
|
|
27
27
|
catch (err) {
|
|
28
|
+
logger.warn(`Error handling request`, err);
|
|
28
29
|
return writeResponse(res, 500, 'There was an error handling your request', contentTypes.text);
|
|
29
30
|
}
|
|
30
31
|
}
|
package/build/utils.d.ts
CHANGED
|
@@ -17,7 +17,15 @@ export declare const buildDir: string;
|
|
|
17
17
|
export declare const tsExtension = ".d.ts";
|
|
18
18
|
export declare const jsonExtension = ".json";
|
|
19
19
|
export declare const jsExtension = ".js";
|
|
20
|
+
export declare const isWin: boolean;
|
|
20
21
|
export declare const id: () => string;
|
|
22
|
+
/**
|
|
23
|
+
* Normalizes a full-path by adding the `file://` protocol if needed.
|
|
24
|
+
*
|
|
25
|
+
* @param filepath - The file path to normalize.
|
|
26
|
+
* @returns The normalized file path.
|
|
27
|
+
*/
|
|
28
|
+
export declare const normalizeFileProtocol: (filepath: string) => string;
|
|
21
29
|
/**
|
|
22
30
|
* Generates a random, Chromium-compliant page ID with "BLESS"
|
|
23
31
|
* prepended. This prepended text signals to other parts of the
|
package/build/utils.js
CHANGED
|
@@ -36,7 +36,22 @@ export const buildDir = path.join(path.resolve(), 'build');
|
|
|
36
36
|
export const tsExtension = '.d.ts';
|
|
37
37
|
export const jsonExtension = '.json';
|
|
38
38
|
export const jsExtension = '.js';
|
|
39
|
+
export const isWin = process.platform === 'win32';
|
|
39
40
|
export const id = () => crypto.randomUUID();
|
|
41
|
+
/**
|
|
42
|
+
* Normalizes a full-path by adding the `file://` protocol if needed.
|
|
43
|
+
*
|
|
44
|
+
* @param filepath - The file path to normalize.
|
|
45
|
+
* @returns The normalized file path.
|
|
46
|
+
*/
|
|
47
|
+
export const normalizeFileProtocol = (filepath) => {
|
|
48
|
+
if (isWin) {
|
|
49
|
+
if (filepath.startsWith('file:///'))
|
|
50
|
+
return filepath;
|
|
51
|
+
return 'file:///' + filepath;
|
|
52
|
+
}
|
|
53
|
+
return filepath;
|
|
54
|
+
};
|
|
40
55
|
/**
|
|
41
56
|
* Generates a random, Chromium-compliant page ID with "BLESS"
|
|
42
57
|
* prepended. This prepended text signals to other parts of the
|
package/docker/chrome/Dockerfile
CHANGED
|
@@ -26,8 +26,9 @@ RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula sele
|
|
|
26
26
|
fonts-ubuntu \
|
|
27
27
|
fonts-wqy-zenhei
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# NOTE it's important to not use npx playwright-core here since it'll likely install
|
|
30
|
+
# a more recent version than we potentially have in our own package.json
|
|
31
|
+
RUN ./node_modules/playwright-core/cli.js install --with-deps chrome &&\
|
|
31
32
|
npm run build &&\
|
|
32
33
|
npm run build:function &&\
|
|
33
34
|
npm prune production &&\
|
|
@@ -26,8 +26,9 @@ RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula sele
|
|
|
26
26
|
fonts-ubuntu \
|
|
27
27
|
fonts-wqy-zenhei
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# NOTE it's important to not use npx playwright-core here since it'll likely install
|
|
30
|
+
# a more recent version than we potentially have in our own package.json
|
|
31
|
+
RUN ./node_modules/playwright-core/cli.js install --with-deps chromium &&\
|
|
31
32
|
npm run build &&\
|
|
32
33
|
npm run build:function &&\
|
|
33
34
|
npm prune production &&\
|
|
@@ -26,8 +26,9 @@ RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula sele
|
|
|
26
26
|
fonts-ubuntu \
|
|
27
27
|
fonts-wqy-zenhei
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# NOTE it's important to not use npx playwright-core here since it'll likely install
|
|
30
|
+
# a more recent version than we potentially have in our own package.json
|
|
31
|
+
RUN ./node_modules/playwright-core/cli.js install --with-deps firefox &&\
|
|
31
32
|
npm run build &&\
|
|
32
33
|
npm prune production &&\
|
|
33
34
|
chown -R blessuser:blessuser $APP_DIR &&\
|
package/docker/multi/Dockerfile
CHANGED
|
@@ -29,12 +29,14 @@ RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula sele
|
|
|
29
29
|
# Chrome stable is only supported on non-ARM builds
|
|
30
30
|
# so it's installation is conditional on whether or not amd64
|
|
31
31
|
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
|
|
32
|
-
./node_modules
|
|
32
|
+
./node_modules/playwright-core/cli.js install --with-deps chrome; \
|
|
33
33
|
else \
|
|
34
34
|
rm -rf ./src/routes/chrome; \
|
|
35
35
|
fi
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
# NOTE it's important to not use npx playwright-core here since it'll likely install
|
|
38
|
+
# a more recent version than we potentially have in our own package.json
|
|
39
|
+
RUN ./node_modules/playwright-core/cli.js install --with-deps chromium firefox webkit &&\
|
|
38
40
|
npm run build &&\
|
|
39
41
|
npm run build:function &&\
|
|
40
42
|
npm prune production &&\
|
package/docker/webkit/Dockerfile
CHANGED
|
@@ -27,8 +27,9 @@ RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula sele
|
|
|
27
27
|
fonts-ubuntu \
|
|
28
28
|
fonts-wqy-zenhei
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
# NOTE it's important to not use npx playwright-core here since it'll likely install
|
|
31
|
+
# a more recent version than we potentially have in our own package.json
|
|
32
|
+
RUN ./node_modules/playwright-core/cli.js install --with-deps webkit &&\
|
|
32
33
|
npm run build &&\
|
|
33
34
|
npm prune production &&\
|
|
34
35
|
chown -R blessuser:blessuser $APP_DIR &&\
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserless.io/browserless",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.0-beta-1",
|
|
4
4
|
"license": "SSPL",
|
|
5
5
|
"description": "The browserless platform",
|
|
6
6
|
"author": "browserless.io",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"dev": "npm run build:dev && env-cmd -f .env node build",
|
|
26
26
|
"install:adblock": "node scripts/install-adblock.js",
|
|
27
27
|
"install:debugger": "node scripts/install-debugger.js",
|
|
28
|
-
"install:browsers": "npx --
|
|
28
|
+
"install:browsers": "npx --no playwright-core install chromium firefox webkit",
|
|
29
29
|
"install:dev": "npm run install:browsers && npm run install:debugger",
|
|
30
|
-
"lint": "eslint . --ext .ts --fix",
|
|
30
|
+
"lint": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint . --ext .ts --fix",
|
|
31
31
|
"prepack": "npm run build:dev",
|
|
32
32
|
"prettier": "prettier '{src,functions,scripts,bin,external,.github}/**/*.{js,ts,json,yml,yaml,md}' --log-level error --write",
|
|
33
33
|
"test": "cross-env DEBUG=quiet mocha",
|
|
@@ -48,21 +48,21 @@
|
|
|
48
48
|
"tsconfig.json"
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"debug": "^4.3.
|
|
51
|
+
"debug": "^4.3.7",
|
|
52
52
|
"del": "^7.0.0",
|
|
53
53
|
"enjoi": "^9.0.1",
|
|
54
|
-
"file-type": "^19.
|
|
54
|
+
"file-type": "^19.5.0",
|
|
55
55
|
"get-port": "^7.1.0",
|
|
56
56
|
"gradient-string": "^2.0.0",
|
|
57
57
|
"http-proxy": "^1.18.1",
|
|
58
|
-
"lighthouse": "^12.2.
|
|
59
|
-
"micromatch": "^4.0.
|
|
58
|
+
"lighthouse": "^12.2.1",
|
|
59
|
+
"micromatch": "^4.0.8",
|
|
60
60
|
"playwright-1.41": "npm:playwright-core@1.41.2",
|
|
61
61
|
"playwright-1.42": "npm:playwright-core@1.42.1",
|
|
62
62
|
"playwright-1.43": "npm:playwright-core@1.43.1",
|
|
63
63
|
"playwright-1.44": "npm:playwright-core@1.44.1",
|
|
64
|
-
"playwright-core": "^1.
|
|
65
|
-
"puppeteer-core": "^23.
|
|
64
|
+
"playwright-core": "^1.47.0",
|
|
65
|
+
"puppeteer-core": "^23.3.0",
|
|
66
66
|
"puppeteer-extra": "^3.3.6",
|
|
67
67
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
68
68
|
"queue": "^7.0.0",
|
|
@@ -70,33 +70,32 @@
|
|
|
70
70
|
"tar-fs": "^3.0.6"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
|
-
"@types/chai": "^4.3.
|
|
73
|
+
"@types/chai": "^4.3.18",
|
|
74
74
|
"@types/debug": "^4.1.12",
|
|
75
75
|
"@types/gradient-string": "^1.1.6",
|
|
76
76
|
"@types/http-proxy": "^1.17.15",
|
|
77
77
|
"@types/micromatch": "^4.0.9",
|
|
78
78
|
"@types/mocha": "^10.0.7",
|
|
79
|
-
"@types/node": "^22.5.
|
|
79
|
+
"@types/node": "^22.5.4",
|
|
80
80
|
"@types/sinon": "^17.0.3",
|
|
81
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
82
|
-
"@typescript-eslint/parser": "^
|
|
81
|
+
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
|
82
|
+
"@typescript-eslint/parser": "^8.5.0",
|
|
83
83
|
"assert": "^2.0.0",
|
|
84
84
|
"chai": "^5.1.1",
|
|
85
85
|
"cross-env": "^7.0.3",
|
|
86
86
|
"env-cmd": "^10.1.0",
|
|
87
87
|
"esbuild": "^0.23.1",
|
|
88
88
|
"esbuild-plugin-polyfill-node": "^0.3.0",
|
|
89
|
-
"eslint": "^
|
|
90
|
-
"eslint-plugin-typescript-sort-keys": "^3.2.0",
|
|
89
|
+
"eslint": "^9.10.0",
|
|
91
90
|
"extract-zip": "^2.0.1",
|
|
92
91
|
"gunzip-maybe": "^1.4.2",
|
|
93
|
-
"marked": "^14.
|
|
92
|
+
"marked": "^14.1.2",
|
|
94
93
|
"mocha": "^10.7.3",
|
|
95
94
|
"move-file": "^3.1.0",
|
|
96
95
|
"prettier": "^3.3.3",
|
|
97
96
|
"sinon": "^18.0.0",
|
|
98
97
|
"ts-node": "^10.9.2",
|
|
99
|
-
"typescript": "^5.
|
|
98
|
+
"typescript": "^5.6.2",
|
|
100
99
|
"typescript-json-schema": "^0.65.1"
|
|
101
100
|
},
|
|
102
101
|
"playwrightVersions": {
|
|
@@ -111,32 +110,23 @@
|
|
|
111
110
|
"root": true,
|
|
112
111
|
"parser": "@typescript-eslint/parser",
|
|
113
112
|
"plugins": [
|
|
114
|
-
"@typescript-eslint"
|
|
115
|
-
"typescript-sort-keys"
|
|
113
|
+
"@typescript-eslint"
|
|
116
114
|
],
|
|
117
115
|
"extends": [
|
|
118
116
|
"eslint:recommended",
|
|
119
117
|
"plugin:@typescript-eslint/eslint-recommended",
|
|
120
|
-
"plugin:@typescript-eslint/recommended"
|
|
121
|
-
"plugin:typescript-sort-keys/recommended"
|
|
118
|
+
"plugin:@typescript-eslint/recommended"
|
|
122
119
|
],
|
|
123
120
|
"ignorePatterns": [
|
|
124
121
|
"node_modules/*",
|
|
122
|
+
"static/*",
|
|
123
|
+
"**.spec.ts",
|
|
125
124
|
"build/*",
|
|
126
125
|
".DS_Store",
|
|
127
126
|
".no-git/*",
|
|
128
127
|
"*.log"
|
|
129
128
|
],
|
|
130
129
|
"rules": {
|
|
131
|
-
"sort-keys": [
|
|
132
|
-
"error",
|
|
133
|
-
"asc",
|
|
134
|
-
{
|
|
135
|
-
"caseSensitive": true,
|
|
136
|
-
"natural": false,
|
|
137
|
-
"minKeys": 2
|
|
138
|
-
}
|
|
139
|
-
],
|
|
140
130
|
"semi": [
|
|
141
131
|
2,
|
|
142
132
|
"always"
|
package/src/browserless.ts
CHANGED
|
@@ -25,8 +25,10 @@ import {
|
|
|
25
25
|
WebKitPlaywright,
|
|
26
26
|
WebSocketRoute,
|
|
27
27
|
availableBrowsers,
|
|
28
|
+
dedent,
|
|
28
29
|
getRouteFiles,
|
|
29
30
|
makeExternalURL,
|
|
31
|
+
normalizeFileProtocol,
|
|
30
32
|
printLogo,
|
|
31
33
|
safeParse,
|
|
32
34
|
} from '@browserless.io/browserless';
|
|
@@ -313,9 +315,7 @@ export class Browserless extends EventEmitter {
|
|
|
313
315
|
}),
|
|
314
316
|
);
|
|
315
317
|
|
|
316
|
-
const wsImport =
|
|
317
|
-
this.config.getIsWin() ? 'file:///' : ''
|
|
318
|
-
}${wsRoute}`;
|
|
318
|
+
const wsImport = normalizeFileProtocol(wsRoute);
|
|
319
319
|
const {
|
|
320
320
|
default: Route,
|
|
321
321
|
}: {
|
|
@@ -357,7 +357,8 @@ export class Browserless extends EventEmitter {
|
|
|
357
357
|
!installedBrowsers.some((b) => b.name === route.browser?.name)
|
|
358
358
|
) {
|
|
359
359
|
throw new Error(
|
|
360
|
-
`Couldn't load route "${route.path}" due to missing browser binary for "${route.browser?.name}"
|
|
360
|
+
dedent(`Couldn't load route "${route.path}" due to missing browser binary for "${route.browser?.name}".
|
|
361
|
+
Installed Browsers: ${installedBrowsers.join(', ')}`),
|
|
361
362
|
);
|
|
362
363
|
}
|
|
363
364
|
});
|
package/src/browsers/index.ts
CHANGED
|
@@ -255,12 +255,16 @@ export class BrowserManager {
|
|
|
255
255
|
},
|
|
256
256
|
});
|
|
257
257
|
if (response.ok) {
|
|
258
|
+
const externalUrl = new URL(serverAddress);
|
|
259
|
+
const protocol = externalUrl.protocol === 'https:' ? 'wss' : 'ws';
|
|
258
260
|
const body = await response.json();
|
|
259
261
|
for (const page of body) {
|
|
262
|
+
const devtoolsFrontendUrl = `/devtools/inspector.html?${protocol}=${externalUrl.host}${externalUrl.pathname}/devtools/page/${page.id}`;
|
|
260
263
|
sessions.push({
|
|
261
264
|
...sessions[0],
|
|
262
265
|
...page,
|
|
263
266
|
browserWSEndpoint: wsEndpoint,
|
|
267
|
+
devtoolsFrontendUrl,
|
|
264
268
|
});
|
|
265
269
|
}
|
|
266
270
|
}
|
|
@@ -299,8 +303,8 @@ export class BrowserManager {
|
|
|
299
303
|
global.setTimeout(() => {
|
|
300
304
|
const session = this.browsers.get(browser);
|
|
301
305
|
if (session) {
|
|
302
|
-
this.log.trace(`Timer hit for "${session.id}"`)
|
|
303
|
-
|
|
306
|
+
this.log.trace(`Timer hit for "${session.id}"`);
|
|
307
|
+
this.close(browser, session);
|
|
304
308
|
}
|
|
305
309
|
}, timeout),
|
|
306
310
|
);
|
package/src/config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { exists, keyLength, untildify } from '@browserless.io/browserless';
|
|
1
|
+
import { exists, isWin as isWindows, keyLength, untildify } from '@browserless.io/browserless';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
3
|
import debug from 'debug';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
@@ -112,8 +112,8 @@ const getDebug = () => {
|
|
|
112
112
|
export class Config extends EventEmitter {
|
|
113
113
|
protected readonly debug = getDebug();
|
|
114
114
|
protected readonly host = process.env.HOST ?? 'localhost';
|
|
115
|
+
protected readonly isWin = isWindows;
|
|
115
116
|
protected external = process.env.PROXY_URL ?? process.env.EXTERNAL;
|
|
116
|
-
protected readonly isWin = process.platform === 'win32';
|
|
117
117
|
|
|
118
118
|
protected port = +(process.env.PORT ?? '3000');
|
|
119
119
|
|
package/src/limiter.spec.ts
CHANGED
package/src/sdk-utils.ts
CHANGED
|
@@ -2,7 +2,24 @@ import { createInterface } from 'readline';
|
|
|
2
2
|
import debug from 'debug';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import {
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
|
|
7
|
+
import { exec } from 'child_process';
|
|
8
|
+
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
|
|
11
|
+
const waitForCommand = async (cmd: string, workingDirectory: string) =>
|
|
12
|
+
new Promise<void>((resolve, reject) =>
|
|
13
|
+
execAsync(cmd, { cwd: workingDirectory }).then(({ stderr }) => {
|
|
14
|
+
if (stderr) {
|
|
15
|
+
return reject(
|
|
16
|
+
`Error running ${cmd}. See output for more details: \n${stderr}`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return resolve();
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
6
23
|
|
|
7
24
|
export const getArgSwitches = () => {
|
|
8
25
|
return process.argv.reduce(
|
|
@@ -41,7 +58,13 @@ export const getSourceFiles = async (cwd: string) => {
|
|
|
41
58
|
const files = await fs.readdir(buildDir, { recursive: true });
|
|
42
59
|
const [httpRoutes, webSocketRoutes] = files.reduce(
|
|
43
60
|
([httpRoutes, webSocketRoutes], file) => {
|
|
61
|
+
const isInRootDir = !file.includes(path.sep);
|
|
44
62
|
const parsed = path.parse(file);
|
|
63
|
+
|
|
64
|
+
if (isInRootDir) {
|
|
65
|
+
return [httpRoutes, webSocketRoutes];
|
|
66
|
+
}
|
|
67
|
+
|
|
45
68
|
if (parsed.name.endsWith('http')) {
|
|
46
69
|
httpRoutes.push(path.join(buildDir, file));
|
|
47
70
|
}
|
|
@@ -81,74 +104,23 @@ export const prompt = async (question: string) => {
|
|
|
81
104
|
});
|
|
82
105
|
};
|
|
83
106
|
|
|
107
|
+
// Exceptions are not caught, since any error would result in a crash regardless
|
|
84
108
|
export const installDependencies = async (
|
|
85
109
|
workingDirectory: string,
|
|
86
110
|
): Promise<void> => {
|
|
87
|
-
await
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (code === 0) {
|
|
93
|
-
return resolve();
|
|
94
|
-
}
|
|
95
|
-
return reject(
|
|
96
|
-
`Error when installing dependencies, see output for more details`,
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
await new Promise<void>((resolve, reject) => {
|
|
101
|
-
spawn(
|
|
102
|
-
'npx',
|
|
103
|
-
'playwright-core install --with-deps chromium firefox webkit'.split(' '),
|
|
104
|
-
{
|
|
105
|
-
cwd: workingDirectory,
|
|
106
|
-
stdio: 'inherit',
|
|
107
|
-
},
|
|
108
|
-
).once('close', (code) => {
|
|
109
|
-
if (code === 0) {
|
|
110
|
-
return resolve();
|
|
111
|
-
}
|
|
112
|
-
return reject(
|
|
113
|
-
`Error when installing dependencies, see output for more details`,
|
|
114
|
-
);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
111
|
+
await waitForCommand('npm install', workingDirectory);
|
|
112
|
+
await waitForCommand(
|
|
113
|
+
'npx playwright-core install --with-deps chromium firefox webkit',
|
|
114
|
+
workingDirectory,
|
|
115
|
+
);
|
|
117
116
|
};
|
|
118
117
|
|
|
119
118
|
export const buildDockerImage = async (
|
|
120
119
|
cmd: string,
|
|
121
120
|
projectDir: string,
|
|
122
|
-
): Promise<void> =>
|
|
123
|
-
new Promise((resolve, reject) => {
|
|
124
|
-
const [docker, ...args] = cmd.split(' ');
|
|
125
|
-
spawn(docker, args, {
|
|
126
|
-
cwd: projectDir,
|
|
127
|
-
stdio: 'inherit',
|
|
128
|
-
}).once('close', (code) => {
|
|
129
|
-
if (code === 0) {
|
|
130
|
-
return resolve();
|
|
131
|
-
}
|
|
132
|
-
return reject(
|
|
133
|
-
`Error when building Docker image, see output for more details`,
|
|
134
|
-
);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
121
|
+
): Promise<void> => waitForCommand(cmd, projectDir);
|
|
137
122
|
|
|
138
123
|
export const buildTypeScript = async (
|
|
139
124
|
buildDir: string,
|
|
140
125
|
projectDir: string,
|
|
141
|
-
): Promise<void> =>
|
|
142
|
-
new Promise((resolve, reject) => {
|
|
143
|
-
spawn('npx', ['tsc', '--outDir', buildDir], {
|
|
144
|
-
cwd: projectDir,
|
|
145
|
-
stdio: 'inherit',
|
|
146
|
-
}).once('close', (code) => {
|
|
147
|
-
if (code === 0) {
|
|
148
|
-
return resolve();
|
|
149
|
-
}
|
|
150
|
-
return reject(
|
|
151
|
-
`Error in building TypeScript, see output for more details`,
|
|
152
|
-
);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
126
|
+
): Promise<void> => waitForCommand(`npx tsc --outDir ${buildDir}`, projectDir);
|
package/src/server.ts
CHANGED
|
@@ -415,7 +415,10 @@ export class HTTPServer extends EventEmitter {
|
|
|
415
415
|
public async shutdown(): Promise<void> {
|
|
416
416
|
this.logger.info(`HTTP Server is shutting down`);
|
|
417
417
|
await new Promise((r) => this.server.close(r));
|
|
418
|
-
|
|
418
|
+
|
|
419
|
+
if (this.server) {
|
|
420
|
+
this.server.removeAllListeners();
|
|
421
|
+
}
|
|
419
422
|
|
|
420
423
|
// @ts-ignore garbage collect this reference
|
|
421
424
|
this.server = null;
|
package/src/utils.ts
CHANGED
|
@@ -64,9 +64,26 @@ export const buildDir: string = path.join(path.resolve(), 'build');
|
|
|
64
64
|
export const tsExtension = '.d.ts';
|
|
65
65
|
export const jsonExtension = '.json';
|
|
66
66
|
export const jsExtension = '.js';
|
|
67
|
+
export const isWin = process.platform === 'win32';
|
|
67
68
|
|
|
68
69
|
export const id = (): string => crypto.randomUUID();
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Normalizes a full-path by adding the `file://` protocol if needed.
|
|
73
|
+
*
|
|
74
|
+
* @param filepath - The file path to normalize.
|
|
75
|
+
* @returns The normalized file path.
|
|
76
|
+
*/
|
|
77
|
+
export const normalizeFileProtocol = (filepath: string) => {
|
|
78
|
+
if (isWin) {
|
|
79
|
+
if (filepath.startsWith('file:///')) return filepath;
|
|
80
|
+
|
|
81
|
+
return 'file:///' + filepath;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return filepath;
|
|
85
|
+
};
|
|
86
|
+
|
|
70
87
|
/**
|
|
71
88
|
* Generates a random, Chromium-compliant page ID with "BLESS"
|
|
72
89
|
* prepended. This prepended text signals to other parts of the
|