@browserless.io/browserless 2.0.0-beta-5 → 2.0.0-beta-7
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/LICENSE +2 -0
- package/README.md +11 -11
- package/bin/browserless.js +169 -11
- package/bin/scaffold/README.md +415 -0
- package/bin/scaffold/package.json +21 -0
- package/bin/scaffold/src/hello-world.http.ts +27 -0
- package/bin/scaffold/tsconfig.json +4 -0
- package/build/browserless.js +18 -15
- package/build/browsers/index.d.ts +2 -18
- package/build/browsers/index.js +43 -14
- package/build/file-system.d.ts +5 -0
- package/build/file-system.js +20 -5
- package/build/file-system.spec.d.ts +1 -0
- package/build/file-system.spec.js +44 -0
- package/build/http.d.ts +3 -3
- package/build/http.js +3 -3
- package/build/router.js +2 -4
- package/build/routes/chromium/http/content-post.body.json +8 -8
- package/build/routes/chromium/http/content-post.d.ts +15 -3
- package/build/routes/chromium/http/content-post.js +14 -15
- package/build/routes/chromium/http/download-post.d.ts +16 -3
- package/build/routes/chromium/http/download-post.js +17 -22
- package/build/routes/chromium/http/function-post.d.ts +16 -3
- package/build/routes/chromium/http/function-post.js +17 -22
- package/build/routes/chromium/http/pdf-post.body.json +8 -8
- package/build/routes/chromium/http/pdf-post.d.ts +15 -3
- package/build/routes/chromium/http/pdf-post.js +19 -15
- package/build/routes/chromium/http/performance.d.ts +15 -3
- package/build/routes/chromium/http/performance.js +15 -23
- package/build/routes/chromium/http/scrape-post.body.json +8 -8
- package/build/routes/chromium/http/scrape-post.d.ts +15 -3
- package/build/routes/chromium/http/scrape-post.js +15 -16
- package/build/routes/chromium/http/screenshot-post.body.json +8 -8
- package/build/routes/chromium/http/screenshot-post.d.ts +15 -3
- package/build/routes/chromium/http/screenshot-post.js +18 -15
- package/build/routes/chromium/tests/websocket.spec.js +20 -1
- package/build/routes/chromium/utils/function/handler.js +2 -2
- package/build/routes/chromium/ws/browser.d.ts +13 -3
- package/build/routes/chromium/ws/browser.js +10 -11
- package/build/routes/chromium/ws/cdp-chromium.d.ts +13 -3
- package/build/routes/chromium/ws/cdp-chromium.js +10 -11
- package/build/routes/chromium/ws/page.d.ts +13 -3
- package/build/routes/chromium/ws/page.js +10 -11
- package/build/routes/chromium/ws/playwright-chromium.d.ts +13 -3
- package/build/routes/chromium/ws/playwright-chromium.js +11 -12
- package/build/routes/firefox/ws/playwright-firefox.d.ts +13 -3
- package/build/routes/firefox/ws/playwright-firefox.js +11 -12
- package/build/routes/management/http/config-get.d.ts +15 -3
- package/build/routes/management/http/config-get.js +15 -20
- package/build/routes/management/http/metrics-get.d.ts +15 -3
- package/build/routes/management/http/metrics-get.js +16 -21
- package/build/routes/management/http/metrics-total-get.d.ts +15 -3
- package/build/routes/management/http/metrics-total-get.js +16 -21
- package/build/routes/management/http/sessions-get.d.ts +15 -3
- package/build/routes/management/http/sessions-get.js +16 -20
- package/build/routes/management/http/static-get.d.ts +15 -3
- package/build/routes/management/http/static-get.js +15 -20
- package/build/routes/webkit/ws/playwright-webkit.d.ts +13 -3
- package/build/routes/webkit/ws/playwright-webkit.js +11 -12
- package/build/server.js +0 -1
- package/build/types.d.ts +48 -38
- package/build/types.js +135 -0
- package/extensions/ublock/1p-filters.html +0 -1
- package/extensions/ublock/3p-filters.html +0 -2
- package/extensions/ublock/_locales/bg/messages.json +6 -6
- package/extensions/ublock/_locales/br_FR/messages.json +14 -14
- package/extensions/ublock/_locales/bs/messages.json +8 -8
- package/extensions/ublock/_locales/ca/messages.json +1 -1
- package/extensions/ublock/_locales/da/messages.json +5 -5
- package/extensions/ublock/_locales/fa/messages.json +1 -1
- package/extensions/ublock/_locales/fi/messages.json +6 -6
- package/extensions/ublock/_locales/hr/messages.json +4 -4
- package/extensions/ublock/_locales/nb/messages.json +1 -1
- package/extensions/ublock/_locales/no/messages.json +1 -1
- package/extensions/ublock/_locales/ro/messages.json +2 -2
- package/extensions/ublock/_locales/ru/messages.json +1 -1
- package/extensions/ublock/_locales/sk/messages.json +1 -1
- package/extensions/ublock/_locales/sv/messages.json +2 -2
- package/extensions/ublock/_locales/te/messages.json +17 -17
- package/extensions/ublock/_locales/vi/messages.json +12 -12
- package/extensions/ublock/_locales/zh_TW/messages.json +13 -13
- package/extensions/ublock/assets/assets.json +3 -3
- package/extensions/ublock/assets/resources/scriptlets.js +218 -97
- package/extensions/ublock/assets/thirdparties/easylist/easylist.txt +3010 -2056
- package/extensions/ublock/assets/thirdparties/easylist/easyprivacy.txt +624 -433
- package/extensions/ublock/assets/thirdparties/pgl.yoyo.org/as/serverlist +93 -24
- package/extensions/ublock/assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat +7 -15
- package/extensions/ublock/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt +795 -777
- package/extensions/ublock/assets/ublock/badware.min.txt +138 -72
- package/extensions/ublock/assets/ublock/filters.min.txt +1929 -2735
- package/extensions/ublock/assets/ublock/privacy.min.txt +57 -26
- package/extensions/ublock/assets/ublock/quick-fixes.min.txt +125 -74
- package/extensions/ublock/assets/ublock/unbreak.min.txt +46 -53
- package/extensions/ublock/css/codemirror.css +8 -7
- package/extensions/ublock/css/dom-inspector.css +40 -0
- package/extensions/ublock/css/logger-ui-inspector.css +7 -1
- package/extensions/ublock/css/logger-ui.css +12 -5
- package/extensions/ublock/css/popup-fenix.css +1 -1
- package/extensions/ublock/devtools.html +1 -0
- package/extensions/ublock/js/1p-filters.js +4 -3
- package/extensions/ublock/js/3p-filters.js +25 -31
- package/extensions/ublock/js/about.js +1 -1
- package/extensions/ublock/js/advanced-settings.js +1 -1
- package/extensions/ublock/js/asset-viewer.js +1 -1
- package/extensions/ublock/js/assets.js +74 -44
- package/extensions/ublock/js/background.js +9 -3
- package/extensions/ublock/js/base64-custom.js +1 -1
- package/extensions/ublock/js/benchmarks.js +1 -1
- package/extensions/ublock/js/biditrie.js +1 -1
- package/extensions/ublock/js/broadcast.js +75 -0
- package/extensions/ublock/js/cachestorage.js +68 -45
- package/extensions/ublock/js/click2load.js +1 -1
- package/extensions/ublock/js/cloud-ui.js +1 -1
- package/extensions/ublock/js/code-viewer.js +1 -1
- package/extensions/ublock/js/codemirror/search-thread.js +1 -1
- package/extensions/ublock/js/codemirror/search.js +1 -1
- package/extensions/ublock/js/codemirror/ubo-dynamic-filtering.js +1 -1
- package/extensions/ublock/js/codemirror/ubo-static-filtering.js +98 -24
- package/extensions/ublock/js/commands.js +1 -1
- package/extensions/ublock/js/console.js +1 -1
- package/extensions/ublock/js/contentscript-extra.js +1 -1
- package/extensions/ublock/js/contentscript.js +1 -3
- package/extensions/ublock/js/contextmenu.js +1 -1
- package/extensions/ublock/js/cosmetic-filtering.js +4 -4
- package/extensions/ublock/js/dashboard-common.js +1 -1
- package/extensions/ublock/js/dashboard.js +1 -1
- package/extensions/ublock/js/devtools.js +23 -15
- package/extensions/ublock/js/diff-updater.js +3 -3
- package/extensions/ublock/js/document-blocked.js +1 -1
- package/extensions/ublock/js/dom-inspector.js +68 -0
- package/extensions/ublock/js/dom.js +1 -1
- package/extensions/ublock/js/dyna-rules.js +1 -1
- package/extensions/ublock/js/dynamic-net-filtering.js +1 -1
- package/extensions/ublock/js/epicker-ui.js +35 -59
- package/extensions/ublock/js/fa-icons.js +1 -1
- package/extensions/ublock/js/filtering-context.js +1 -1
- package/extensions/ublock/js/filtering-engines.js +1 -1
- package/extensions/ublock/js/hnswitches.js +1 -1
- package/extensions/ublock/js/hntrie.js +1 -1
- package/extensions/ublock/js/html-filtering.js +1 -1
- package/extensions/ublock/js/httpheader-filtering.js +1 -1
- package/extensions/ublock/js/i18n.js +1 -1
- package/extensions/ublock/js/is-webrtc-supported.js +1 -1
- package/extensions/ublock/js/logger-ui-inspector.js +203 -145
- package/extensions/ublock/js/logger-ui.js +21 -5
- package/extensions/ublock/js/logger.js +6 -2
- package/extensions/ublock/js/lz4.js +2 -2
- package/extensions/ublock/js/messaging.js +266 -166
- package/extensions/ublock/js/mrucache.js +58 -0
- package/extensions/ublock/js/pagestore.js +1 -1
- package/extensions/ublock/js/popup-fenix.js +2 -1
- package/extensions/ublock/js/redirect-engine.js +1 -1
- package/extensions/ublock/js/redirect-resources.js +1 -12
- package/extensions/ublock/js/reverselookup-worker.js +1 -1
- package/extensions/ublock/js/reverselookup.js +1 -1
- package/extensions/ublock/js/scriptlet-filtering-core.js +300 -0
- package/extensions/ublock/js/scriptlet-filtering.js +122 -350
- package/extensions/ublock/js/scriptlets/cosmetic-logger.js +36 -47
- package/extensions/ublock/js/scriptlets/cosmetic-off.js +1 -1
- package/extensions/ublock/js/scriptlets/cosmetic-on.js +1 -1
- package/extensions/ublock/js/scriptlets/cosmetic-report.js +1 -1
- package/extensions/ublock/js/scriptlets/dom-inspector.js +341 -323
- package/extensions/ublock/js/scriptlets/dom-survey-elements.js +1 -1
- package/extensions/ublock/js/scriptlets/dom-survey-scripts.js +1 -1
- package/extensions/ublock/js/scriptlets/epicker.js +80 -89
- package/extensions/ublock/js/scriptlets/load-3p-css.js +1 -1
- package/extensions/ublock/js/scriptlets/load-large-media-all.js +1 -1
- package/extensions/ublock/js/scriptlets/load-large-media-interactive.js +1 -1
- package/extensions/ublock/js/scriptlets/noscript-spoof.js +1 -1
- package/extensions/ublock/js/scriptlets/should-inject-contentscript.js +1 -1
- package/extensions/ublock/js/scriptlets/subscriber.js +1 -1
- package/extensions/ublock/js/scriptlets/updater.js +20 -3
- package/extensions/ublock/js/settings.js +1 -1
- package/extensions/ublock/js/start.js +19 -20
- package/extensions/ublock/js/static-dnr-filtering.js +1 -1
- package/extensions/ublock/js/static-ext-filtering-db.js +1 -1
- package/extensions/ublock/js/static-ext-filtering.js +1 -1
- package/extensions/ublock/js/static-filtering-io.js +1 -1
- package/extensions/ublock/js/static-filtering-parser.js +5 -3
- package/extensions/ublock/js/static-net-filtering.js +57 -37
- package/extensions/ublock/js/storage.js +49 -29
- package/extensions/ublock/js/support.js +4 -4
- package/extensions/ublock/js/tab.js +1 -1
- package/extensions/ublock/js/tasks.js +1 -1
- package/extensions/ublock/js/text-encode.js +1 -1
- package/extensions/ublock/js/text-utils.js +1 -1
- package/extensions/ublock/js/theme.js +1 -1
- package/extensions/ublock/js/traffic.js +2 -1
- package/extensions/ublock/js/ublock.js +15 -11
- package/extensions/ublock/js/uri-utils.js +1 -1
- package/extensions/ublock/js/url-net-filtering.js +1 -1
- package/extensions/ublock/js/utils.js +1 -73
- package/extensions/ublock/js/vapi-background-ext.js +1 -1
- package/extensions/ublock/js/vapi-background.js +92 -83
- package/extensions/ublock/js/vapi-client.js +4 -33
- package/extensions/ublock/js/vapi-common.js +16 -30
- package/extensions/ublock/js/vapi.js +1 -1
- package/extensions/ublock/js/wasm/biditrie.wat +1 -1
- package/extensions/ublock/js/wasm/hntrie.wat +1 -1
- package/extensions/ublock/js/webext.js +1 -1
- package/extensions/ublock/js/whitelist.js +1 -1
- package/extensions/ublock/logger-ui.html +2 -2
- package/extensions/ublock/manifest.json +1 -1
- package/extensions/ublock/support.html +0 -1
- package/extensions/ublock/web_accessible_resources/dom-inspector.html +25 -0
- package/extensions/ublock/web_accessible_resources/epicker-ui.html +0 -1
- package/extensions/ublock/web_accessible_resources/googletagservices_gpt.js +1 -0
- package/package.json +7 -19
- package/scripts/build-open-api.js +7 -4
- package/src/browserless.ts +42 -18
- package/src/browsers/index.ts +48 -20
- package/src/file-system.spec.ts +58 -0
- package/src/file-system.ts +36 -8
- package/src/http.ts +3 -3
- package/src/router.ts +2 -6
- package/src/routes/chromium/http/content-post.ts +13 -16
- package/src/routes/chromium/http/download-post.ts +16 -27
- package/src/routes/chromium/http/function-post.ts +16 -25
- package/src/routes/chromium/http/pdf-post.ts +19 -15
- package/src/routes/chromium/http/performance.ts +14 -26
- package/src/routes/chromium/http/scrape-post.ts +14 -16
- package/src/routes/chromium/http/screenshot-post.ts +18 -15
- package/src/routes/chromium/tests/websocket.spec.ts +28 -1
- package/src/routes/chromium/utils/function/handler.ts +2 -1
- package/src/routes/chromium/ws/browser.ts +10 -12
- package/src/routes/chromium/ws/cdp-chromium.ts +10 -12
- package/src/routes/chromium/ws/page.ts +10 -12
- package/src/routes/chromium/ws/playwright-chromium.ts +10 -12
- package/src/routes/firefox/ws/playwright-firefox.ts +10 -12
- package/src/routes/management/http/config-get.ts +14 -23
- package/src/routes/management/http/metrics-get.ts +15 -24
- package/src/routes/management/http/metrics-total-get.ts +15 -26
- package/src/routes/management/http/sessions-get.ts +15 -23
- package/src/routes/management/http/static-get.ts +14 -22
- package/src/routes/webkit/ws/playwright-webkit.ts +10 -12
- package/src/server.ts +0 -1
- package/src/types.ts +59 -45
- package/static/docs/browserless-logo-inline.svg +1 -0
- package/static/docs/index.html +27 -0
- package/static/docs/swagger.json +33 -33
- package/static/function/client.js +626 -78
- package/extensions/ublock/js/vapi-client-extra.js +0 -312
- package/extensions/ublock/web_accessible_resources/addthis_widget.js +0 -39
- package/extensions/ublock/web_accessible_resources/ligatus_angular-tag.js +0 -29
- package/extensions/ublock/web_accessible_resources/monkeybroker.js +0 -43
- package/extensions/ublock/web_accessible_resources/mxpnl_mixpanel.js +0 -51
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserless.io/browserless",
|
|
3
|
-
"version": "2.0.0-beta-
|
|
3
|
+
"version": "2.0.0-beta-7",
|
|
4
4
|
"license": "SSPL",
|
|
5
5
|
"description": "The browserless platform",
|
|
6
6
|
"author": "browserless.io",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"install:dev": "npm run install:browsers && npm run install:cdp-json",
|
|
30
30
|
"lint": "eslint . --ext .ts --fix",
|
|
31
31
|
"prepack": "npm run build:dev",
|
|
32
|
-
"prettier": "prettier '{src,functions,scripts,bin,external}/**/*.{js,ts,json}' --log-level error --write",
|
|
32
|
+
"prettier": "prettier '{src,functions,scripts,bin,external,bin}/**/*.{js,ts,json}' --log-level error --write",
|
|
33
33
|
"test": "cross-env DEBUG=quiet mocha"
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
@@ -71,15 +71,15 @@
|
|
|
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.10.
|
|
74
|
+
"@types/node": "^20.10.8",
|
|
75
75
|
"@types/sinon": "^17.0.2",
|
|
76
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
77
|
-
"@typescript-eslint/parser": "^6.
|
|
76
|
+
"@typescript-eslint/eslint-plugin": "^6.18.1",
|
|
77
|
+
"@typescript-eslint/parser": "^6.18.1",
|
|
78
78
|
"assert": "^2.0.0",
|
|
79
|
-
"chai": "^
|
|
79
|
+
"chai": "^5.0.0",
|
|
80
80
|
"cross-env": "^7.0.3",
|
|
81
81
|
"env-cmd": "^10.1.0",
|
|
82
|
-
"esbuild": "^0.19.
|
|
82
|
+
"esbuild": "^0.19.11",
|
|
83
83
|
"esbuild-plugin-polyfill-node": "^0.3.0",
|
|
84
84
|
"eslint": "^8.56.0",
|
|
85
85
|
"eslint-plugin-import": "^2.29.1",
|
|
@@ -158,18 +158,6 @@
|
|
|
158
158
|
"timeout": 30000,
|
|
159
159
|
"slow": 5000
|
|
160
160
|
},
|
|
161
|
-
"nodemonConfig": {
|
|
162
|
-
"ignoreRoot": [
|
|
163
|
-
".git",
|
|
164
|
-
".no-git"
|
|
165
|
-
],
|
|
166
|
-
"watch": [
|
|
167
|
-
"src"
|
|
168
|
-
],
|
|
169
|
-
"exec": "npx tsc && node ./build/index.js",
|
|
170
|
-
"ext": "ts json",
|
|
171
|
-
"signal": "SIGTERM"
|
|
172
|
-
},
|
|
173
161
|
"prettier": {
|
|
174
162
|
"semi": true,
|
|
175
163
|
"trailingComma": "all",
|
|
@@ -56,17 +56,19 @@ const buildOpenAPI = async (
|
|
|
56
56
|
const changelog = marked.parse(
|
|
57
57
|
(await fs.readFile('CHANGELOG.md').catch(() => '')).toString(),
|
|
58
58
|
);
|
|
59
|
+
|
|
59
60
|
const [httpRoutes, wsRoutes] = await getRouteFiles(new Config());
|
|
60
61
|
const swaggerJSON = {
|
|
61
62
|
customSiteTitle: 'Browserless Documentation',
|
|
62
63
|
definitions: {},
|
|
63
64
|
info: {
|
|
64
|
-
|
|
65
|
+
// Concatenation necessary for Changelog to show up in sidebar
|
|
66
|
+
description: readme + `\n# Changelog\n` + changelog,
|
|
65
67
|
title: 'Browserless',
|
|
66
68
|
version: JSON.parse(packageJSON.toString()).version,
|
|
67
69
|
'x-logo': {
|
|
68
70
|
altText: 'browserless logo',
|
|
69
|
-
url: './docs/browserless-logo.
|
|
71
|
+
url: './docs/browserless-logo-inline.svg',
|
|
70
72
|
},
|
|
71
73
|
},
|
|
72
74
|
openapi: '3.0.0',
|
|
@@ -86,10 +88,11 @@ const buildOpenAPI = async (
|
|
|
86
88
|
.sort()
|
|
87
89
|
.map(async (routeModule) => {
|
|
88
90
|
const routeImport = `${isWin ? 'file:///' : ''}${routeModule}`;
|
|
89
|
-
const { default:
|
|
90
|
-
if (!
|
|
91
|
+
const { default: Route } = await import(routeImport);
|
|
92
|
+
if (!Route) {
|
|
91
93
|
throw new Error(`Invalid route file to import docs ${routeModule}`);
|
|
92
94
|
}
|
|
95
|
+
const route = new Route();
|
|
93
96
|
const { name } = parse(routeModule);
|
|
94
97
|
const body = routeModule.replace('.js', '.body.json');
|
|
95
98
|
const query = routeModule.replace('.js', '.query.json');
|
package/src/browserless.ts
CHANGED
|
@@ -27,6 +27,10 @@ import { userInfo } from 'os';
|
|
|
27
27
|
|
|
28
28
|
const routeSchemas = ['body', 'query'];
|
|
29
29
|
|
|
30
|
+
type Implements<T> = {
|
|
31
|
+
new (...args: unknown[]): T;
|
|
32
|
+
};
|
|
33
|
+
|
|
30
34
|
export class Browserless {
|
|
31
35
|
protected debug: debug.Debugger = createLogger('index');
|
|
32
36
|
protected browserManager: BrowserManager;
|
|
@@ -181,16 +185,25 @@ export class Browserless {
|
|
|
181
185
|
this.config.getIsWin() ? 'file:///' : ''
|
|
182
186
|
}${httpRoute}`;
|
|
183
187
|
const logger = createLogger(`http:${name}`);
|
|
184
|
-
const {
|
|
188
|
+
const {
|
|
189
|
+
default: Route,
|
|
190
|
+
}: { default: Implements<HTTPRoute> | Implements<BrowserHTTPRoute> } =
|
|
185
191
|
await import(routeImport + `?cb=${Date.now()}`);
|
|
186
|
-
|
|
192
|
+
const route = new Route(
|
|
193
|
+
this.browserManager,
|
|
194
|
+
this.config,
|
|
195
|
+
this.fileSystem,
|
|
196
|
+
logger,
|
|
197
|
+
this.metrics,
|
|
198
|
+
this.monitoring,
|
|
199
|
+
);
|
|
187
200
|
route.bodySchema = safeParse(bodySchema);
|
|
188
201
|
route.querySchema = safeParse(querySchema);
|
|
189
|
-
route.
|
|
190
|
-
route.
|
|
191
|
-
route.
|
|
192
|
-
route.
|
|
193
|
-
route.
|
|
202
|
+
route.config = () => this.config;
|
|
203
|
+
route.metrics = () => this.metrics;
|
|
204
|
+
route.monitoring = () => this.monitoring;
|
|
205
|
+
route.fileSystem = () => this.fileSystem;
|
|
206
|
+
route.debug = () => logger;
|
|
194
207
|
|
|
195
208
|
httpRoutes.push(route);
|
|
196
209
|
}
|
|
@@ -215,17 +228,27 @@ export class Browserless {
|
|
|
215
228
|
}${wsRoute}`;
|
|
216
229
|
const logger = createLogger(`ws:${name}`);
|
|
217
230
|
const {
|
|
218
|
-
default:
|
|
219
|
-
}: {
|
|
220
|
-
|
|
221
|
-
|
|
231
|
+
default: Route,
|
|
232
|
+
}: {
|
|
233
|
+
default:
|
|
234
|
+
| Implements<WebSocketRoute>
|
|
235
|
+
| Implements<BrowserWebsocketRoute>;
|
|
236
|
+
} = await import(wsImport + `?cb=${Date.now()}`);
|
|
222
237
|
|
|
238
|
+
const route = new Route(
|
|
239
|
+
this.browserManager,
|
|
240
|
+
this.config,
|
|
241
|
+
this.fileSystem,
|
|
242
|
+
logger,
|
|
243
|
+
this.metrics,
|
|
244
|
+
this.monitoring,
|
|
245
|
+
);
|
|
223
246
|
route.querySchema = safeParse(querySchema);
|
|
224
|
-
route.
|
|
225
|
-
route.
|
|
226
|
-
route.
|
|
227
|
-
route.
|
|
228
|
-
route.
|
|
247
|
+
route.config = () => this.config;
|
|
248
|
+
route.metrics = () => this.metrics;
|
|
249
|
+
route.monitoring = () => this.monitoring;
|
|
250
|
+
route.fileSystem = () => this.fileSystem;
|
|
251
|
+
route.debug = () => logger;
|
|
229
252
|
|
|
230
253
|
wsRoutes.push(route);
|
|
231
254
|
}
|
|
@@ -234,11 +257,12 @@ export class Browserless {
|
|
|
234
257
|
// Validate that browsers are installed and route paths are unique
|
|
235
258
|
[...httpRoutes, ...wsRoutes].forEach((route) => {
|
|
236
259
|
if (
|
|
260
|
+
'browser' in route &&
|
|
237
261
|
route.browser &&
|
|
238
|
-
!installedBrowsers.some((b) => b.name === route.browser
|
|
262
|
+
!installedBrowsers.some((b) => b.name === route.browser?.name)
|
|
239
263
|
) {
|
|
240
264
|
throw new Error(
|
|
241
|
-
`Couldn't load route "${route.path}" due to missing browser of "${route.browser
|
|
265
|
+
`Couldn't load route "${route.path}" due to missing browser of "${route.browser?.name}"`,
|
|
242
266
|
);
|
|
243
267
|
}
|
|
244
268
|
});
|
package/src/browsers/index.ts
CHANGED
|
@@ -82,27 +82,52 @@ export class BrowserManager {
|
|
|
82
82
|
return dataDirPath;
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
private generateSessionJson = async (
|
|
86
86
|
browser: BrowserInstance,
|
|
87
87
|
session: BrowserlessSession,
|
|
88
88
|
) => {
|
|
89
89
|
const serverAddress = this.config.getExternalAddress();
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
91
|
+
const sessions = [
|
|
92
|
+
{
|
|
93
|
+
...session,
|
|
94
|
+
browser: browser.constructor.name,
|
|
95
|
+
browserId: browser.wsEndpoint()?.split('/').pop(),
|
|
96
|
+
initialConnectURL: new URL(session.initialConnectURL, serverAddress)
|
|
97
|
+
.href,
|
|
98
|
+
killURL: session.id
|
|
99
|
+
? makeExternalURL(
|
|
100
|
+
serverAddress,
|
|
101
|
+
HTTPManagementRoutes.sessions,
|
|
102
|
+
session.id,
|
|
103
|
+
)
|
|
104
|
+
: null,
|
|
105
|
+
running: browser.isRunning(),
|
|
106
|
+
timeAliveMs: Date.now() - session.startedOn,
|
|
107
|
+
type: 'browser',
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const wsEndpoint = browser.wsEndpoint();
|
|
112
|
+
if (browser.constructor.name === 'CDPChromium' && wsEndpoint) {
|
|
113
|
+
const port = new URL(wsEndpoint).port;
|
|
114
|
+
const response = await fetch(`http://127.0.0.1:${port}/json/list`, {
|
|
115
|
+
headers: {
|
|
116
|
+
Host: '127.0.0.1',
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
if (response.ok) {
|
|
120
|
+
const body = await response.json();
|
|
121
|
+
for (const page of body) {
|
|
122
|
+
sessions.push({
|
|
123
|
+
...sessions[0],
|
|
124
|
+
...page,
|
|
125
|
+
browserWSEndpoint: wsEndpoint,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return sessions;
|
|
106
131
|
};
|
|
107
132
|
|
|
108
133
|
public close = async (
|
|
@@ -129,9 +154,12 @@ export class BrowserManager {
|
|
|
129
154
|
public getAllSessions = async (): Promise<BrowserlessSessionJSON[]> => {
|
|
130
155
|
const sessions = Array.from(this.browsers);
|
|
131
156
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
const formattedSessions: BrowserlessSessionJSON[] = [];
|
|
158
|
+
for (const [browser, session] of sessions) {
|
|
159
|
+
const formattedSession = await this.generateSessionJson(browser, session);
|
|
160
|
+
formattedSessions.push(...formattedSession);
|
|
161
|
+
}
|
|
162
|
+
return formattedSessions;
|
|
135
163
|
};
|
|
136
164
|
|
|
137
165
|
public complete = async (browser: BrowserInstance): Promise<void> => {
|
|
@@ -208,7 +236,7 @@ export class BrowserManager {
|
|
|
208
236
|
const manualUserDataDir =
|
|
209
237
|
launchOptions.args
|
|
210
238
|
?.find((arg) => arg.includes('--user-data-dir='))
|
|
211
|
-
?.split('=')[
|
|
239
|
+
?.split('=')[1] || (launchOptions as CDPLaunchOptions).userDataDir;
|
|
212
240
|
|
|
213
241
|
// Always specify a user-data-dir since plugins can "inject" their own
|
|
214
242
|
// unless it's playwright which takes care of its own data-dirs
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Config, FileSystem, sleep } from '@browserless.io/browserless';
|
|
2
|
+
import { readFile, unlink } from 'fs/promises';
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
|
|
5
|
+
const filePath = '/tmp/_browserless_test_fs_';
|
|
6
|
+
|
|
7
|
+
describe.only('File-System', () => {
|
|
8
|
+
afterEach(async () => unlink(filePath));
|
|
9
|
+
|
|
10
|
+
it('saves and encodes files', async () => {
|
|
11
|
+
const mySecretContents = 'pony-foo';
|
|
12
|
+
const config = new Config();
|
|
13
|
+
config.setToken('browserless.io');
|
|
14
|
+
const f = new FileSystem(config);
|
|
15
|
+
|
|
16
|
+
await f.append(filePath, mySecretContents);
|
|
17
|
+
|
|
18
|
+
expect(await f.read(filePath)).to.eql([mySecretContents]);
|
|
19
|
+
const rawText = (await readFile(filePath)).toString();
|
|
20
|
+
|
|
21
|
+
expect(rawText.toString()).to.not.include(mySecretContents);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('appends newlines to files', async () => {
|
|
25
|
+
const mySecretContents = 'pony-foo';
|
|
26
|
+
const moreSecretContents = 'pony-pony-foo-foo';
|
|
27
|
+
const config = new Config();
|
|
28
|
+
config.setToken('browserless.io');
|
|
29
|
+
const f = new FileSystem(config);
|
|
30
|
+
|
|
31
|
+
await f.append(filePath, mySecretContents);
|
|
32
|
+
|
|
33
|
+
expect(await f.read(filePath)).to.eql([mySecretContents]);
|
|
34
|
+
|
|
35
|
+
await f.append(filePath, moreSecretContents);
|
|
36
|
+
|
|
37
|
+
expect(await f.read(filePath)).to.eql([
|
|
38
|
+
mySecretContents,
|
|
39
|
+
moreSecretContents,
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('re-encodes files on token changes', async () => {
|
|
44
|
+
const config = new Config();
|
|
45
|
+
config.setToken('browserless.io');
|
|
46
|
+
const f = new FileSystem(config);
|
|
47
|
+
const mySecretContents = 'pony-foo';
|
|
48
|
+
|
|
49
|
+
await f.append(filePath, mySecretContents);
|
|
50
|
+
const oldText = (await readFile(filePath)).toString();
|
|
51
|
+
config.setToken('super-browserless-64');
|
|
52
|
+
await sleep(200);
|
|
53
|
+
const newText = (await readFile(filePath)).toString();
|
|
54
|
+
|
|
55
|
+
expect(oldText).to.not.equal(newText);
|
|
56
|
+
expect(await f.read(filePath)).to.eql([mySecretContents]);
|
|
57
|
+
});
|
|
58
|
+
});
|
package/src/file-system.ts
CHANGED
|
@@ -1,10 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Config,
|
|
3
|
+
createLogger,
|
|
4
|
+
decrypt,
|
|
5
|
+
encrypt,
|
|
6
|
+
} from '@browserless.io/browserless';
|
|
2
7
|
import { readFile, writeFile } from 'fs/promises';
|
|
3
8
|
|
|
4
9
|
export class FileSystem {
|
|
5
10
|
protected fsMap: Map<string, string[]> = new Map();
|
|
11
|
+
protected currentAESKey: Buffer;
|
|
12
|
+
protected log = createLogger('file-system');
|
|
6
13
|
|
|
7
|
-
constructor(protected config: Config) {
|
|
14
|
+
constructor(protected config: Config) {
|
|
15
|
+
this.currentAESKey = config.getAESKey();
|
|
16
|
+
this.config.on('token', this.handleTokenChange);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private handleTokenChange = async () => {
|
|
20
|
+
this.log(`Token has changed, updating file-system contents`);
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
const newAESKey = this.config.getAESKey();
|
|
23
|
+
await Promise.all(
|
|
24
|
+
Array.from(this.fsMap).map(async ([filePath, contents]) => {
|
|
25
|
+
const newlyEncoded = encrypt(
|
|
26
|
+
contents.join('\n'),
|
|
27
|
+
Buffer.from(newAESKey),
|
|
28
|
+
);
|
|
29
|
+
return writeFile(filePath, newlyEncoded);
|
|
30
|
+
}),
|
|
31
|
+
).catch((e) => {
|
|
32
|
+
this.log(`Error in setting new token: "${e}"`);
|
|
33
|
+
});
|
|
34
|
+
this.log(`Successfully updated file encodings in ${Date.now() - start}ms`);
|
|
35
|
+
this.currentAESKey = this.config.getAESKey();
|
|
36
|
+
};
|
|
8
37
|
|
|
9
38
|
/**
|
|
10
39
|
* Appends contents to a file-path for persistance. File contents are
|
|
@@ -20,9 +49,10 @@ export class FileSystem {
|
|
|
20
49
|
|
|
21
50
|
contents.push(newContent);
|
|
22
51
|
this.fsMap.set(path, contents);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
52
|
+
const encoded = await encrypt(
|
|
53
|
+
contents.join('\n'),
|
|
54
|
+
Buffer.from(this.currentAESKey),
|
|
55
|
+
);
|
|
26
56
|
|
|
27
57
|
return writeFile(path, encoded.toString());
|
|
28
58
|
};
|
|
@@ -40,11 +70,9 @@ export class FileSystem {
|
|
|
40
70
|
if (hasKey) {
|
|
41
71
|
return this.fsMap.get(path) as string[];
|
|
42
72
|
}
|
|
43
|
-
|
|
44
|
-
const key = this.config.getAESKey();
|
|
45
73
|
const contents = (await readFile(path).catch(() => '')).toString();
|
|
46
74
|
const splitContents = contents.length
|
|
47
|
-
? (await decrypt(contents, Buffer.from(
|
|
75
|
+
? (await decrypt(contents, Buffer.from(this.currentAESKey))).split('\n')
|
|
48
76
|
: [];
|
|
49
77
|
|
|
50
78
|
this.fsMap.set(path, splitContents);
|
package/src/http.ts
CHANGED
|
@@ -107,9 +107,9 @@ export enum HTTPManagementRoutes {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
export enum APITags {
|
|
110
|
-
'browserAPI' = 'Browser APIs',
|
|
111
|
-
'browserWS' = 'Browser
|
|
112
|
-
'management' = 'Management APIs',
|
|
110
|
+
'browserAPI' = 'Browser REST APIs',
|
|
111
|
+
'browserWS' = 'Browser WebSocket APIs',
|
|
112
|
+
'management' = 'Management REST APIs',
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export interface Request extends http.IncomingMessage {
|
package/src/router.ts
CHANGED
|
@@ -67,7 +67,7 @@ export class Router {
|
|
|
67
67
|
return Promise.resolve();
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
if (route.browser) {
|
|
70
|
+
if ('browser' in route && route.browser) {
|
|
71
71
|
const browser = await this.browserManager.getBrowserForRequest(
|
|
72
72
|
req,
|
|
73
73
|
route,
|
|
@@ -111,7 +111,7 @@ export class Router {
|
|
|
111
111
|
return Promise.resolve();
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
if (route.browser) {
|
|
114
|
+
if ('browser' in route && route.browser) {
|
|
115
115
|
const browser = await this.browserManager.getBrowserForRequest(
|
|
116
116
|
req,
|
|
117
117
|
route,
|
|
@@ -146,8 +146,6 @@ export class Router {
|
|
|
146
146
|
`Registering HTTP ${route.method.toUpperCase()} ${route.path}`,
|
|
147
147
|
);
|
|
148
148
|
|
|
149
|
-
route.getBrowserManager = () => this.browserManager;
|
|
150
|
-
|
|
151
149
|
const bound = route.handler.bind(route);
|
|
152
150
|
const wrapped = this.wrapHTTPHandler(route, bound);
|
|
153
151
|
|
|
@@ -170,8 +168,6 @@ export class Router {
|
|
|
170
168
|
): WebSocketRoute | BrowserWebsocketRoute {
|
|
171
169
|
this.verbose(`Registering WebSocket "${route.path}"`);
|
|
172
170
|
|
|
173
|
-
route.getBrowserManager = () => this.browserManager;
|
|
174
|
-
|
|
175
171
|
const bound = route.handler.bind(route);
|
|
176
172
|
const wrapped = this.wrapWebSocketHandler(route, bound);
|
|
177
173
|
|
|
@@ -61,15 +61,17 @@ export type QuerySchema = SystemQueryParameters & {
|
|
|
61
61
|
launch?: CDPLaunchOptions | string;
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
accepts
|
|
66
|
-
auth
|
|
67
|
-
browser
|
|
68
|
-
concurrency
|
|
69
|
-
contentTypes
|
|
70
|
-
description
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
export default class ContentPostRoute extends BrowserHTTPRoute {
|
|
65
|
+
accepts = [contentTypes.json];
|
|
66
|
+
auth = true;
|
|
67
|
+
browser = CDPChromium;
|
|
68
|
+
concurrency = true;
|
|
69
|
+
contentTypes = [contentTypes.html];
|
|
70
|
+
description = `A JSON-based API. Given a "url" or "html" field, runs and returns HTML content after the page has loaded and JavaScript has parsed.`;
|
|
71
|
+
method = Methods.post;
|
|
72
|
+
path = HTTPRoutes.content;
|
|
73
|
+
tags = [APITags.browserAPI];
|
|
74
|
+
handler = async (
|
|
73
75
|
req: Request,
|
|
74
76
|
res: ServerResponse,
|
|
75
77
|
browser: BrowserInstance,
|
|
@@ -229,10 +231,5 @@ const route: BrowserHTTPRoute = {
|
|
|
229
231
|
page.close().catch(noop);
|
|
230
232
|
|
|
231
233
|
return writeResponse(res, 200, markup, contentTypes.html);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
path: HTTPRoutes.content,
|
|
235
|
-
tags: [APITags.browserAPI],
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
export default route;
|
|
234
|
+
};
|
|
235
|
+
}
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
Methods,
|
|
9
9
|
NotFound,
|
|
10
10
|
Request,
|
|
11
|
-
ServerError,
|
|
12
11
|
SystemQueryParameters,
|
|
13
12
|
contentTypes,
|
|
14
13
|
dedent,
|
|
@@ -41,35 +40,30 @@ export interface QuerySchema extends SystemQueryParameters {
|
|
|
41
40
|
*/
|
|
42
41
|
export type ResponseSchema = unknown;
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
accepts
|
|
46
|
-
auth
|
|
47
|
-
browser
|
|
48
|
-
concurrency
|
|
49
|
-
contentTypes
|
|
50
|
-
description
|
|
43
|
+
export default class DownloadPost extends BrowserHTTPRoute {
|
|
44
|
+
accepts = [contentTypes.json, contentTypes.javascript];
|
|
45
|
+
auth = true;
|
|
46
|
+
browser = CDPChromium;
|
|
47
|
+
concurrency = true;
|
|
48
|
+
contentTypes = [contentTypes.any];
|
|
49
|
+
description = dedent(`
|
|
51
50
|
A JSON or JavaScript content-type API for returning files Chrome has downloaded during
|
|
52
51
|
the execution of puppeteer code, which is ran inside context of the browser.
|
|
53
52
|
Browserless sets up a blank page, a fresh download directory, injects your puppeteer code, and then executes it.
|
|
54
53
|
You can load external libraries via the "import" syntax, and import ESM-style modules
|
|
55
54
|
that are written for execution inside of the browser. Once your script is finished, any
|
|
56
|
-
downloaded files from Chromium are returned back with the appropriate content-type header.`)
|
|
57
|
-
|
|
55
|
+
downloaded files from Chromium are returned back with the appropriate content-type header.`);
|
|
56
|
+
method = Methods.post;
|
|
57
|
+
path = HTTPRoutes.download;
|
|
58
|
+
tags = [APITags.browserAPI];
|
|
59
|
+
handler = async (
|
|
58
60
|
req: Request,
|
|
59
61
|
res: ServerResponse,
|
|
60
62
|
browser: BrowserInstance,
|
|
61
63
|
): Promise<void> =>
|
|
62
64
|
new Promise(async (resolve, reject) => {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
if (!getConfig || !getDebug) {
|
|
66
|
-
return reject(
|
|
67
|
-
new ServerError(`Couldn't load configuration for request`),
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const debug = getDebug();
|
|
72
|
-
const config = getConfig();
|
|
65
|
+
const debug = this.debug();
|
|
66
|
+
const config = this.config();
|
|
73
67
|
const downloadPath = path.join(
|
|
74
68
|
await config.getDownloadsDir(),
|
|
75
69
|
`.browserless.download.${id()}`,
|
|
@@ -153,10 +147,5 @@ const route: BrowserHTTPRoute = {
|
|
|
153
147
|
return resolve();
|
|
154
148
|
})
|
|
155
149
|
.pipe(res);
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
path: HTTPRoutes.download,
|
|
159
|
-
tags: [APITags.browserAPI],
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
export default route;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
HTTPRoutes,
|
|
9
9
|
Methods,
|
|
10
10
|
Request,
|
|
11
|
-
ServerError,
|
|
12
11
|
SystemQueryParameters,
|
|
13
12
|
contentTypes,
|
|
14
13
|
dedent,
|
|
@@ -38,31 +37,28 @@ export interface QuerySchema extends SystemQueryParameters {
|
|
|
38
37
|
*/
|
|
39
38
|
export type ResponseSchema = unknown;
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
accepts
|
|
43
|
-
auth
|
|
44
|
-
browser
|
|
45
|
-
concurrency
|
|
46
|
-
contentTypes
|
|
47
|
-
description
|
|
40
|
+
export default class FunctionPost extends BrowserHTTPRoute {
|
|
41
|
+
accepts = [contentTypes.json, contentTypes.javascript];
|
|
42
|
+
auth = true;
|
|
43
|
+
browser = CDPChromium;
|
|
44
|
+
concurrency = true;
|
|
45
|
+
contentTypes = [contentTypes.any];
|
|
46
|
+
description = dedent(`
|
|
48
47
|
A JSON or JavaScript content-type API for running puppeteer code in the browser's context.
|
|
49
48
|
Browserless sets up a blank page, injects your puppeteer code, and runs it.
|
|
50
49
|
You can optionally load external libraries via the "import" module that are meant for browser usage.
|
|
51
50
|
Values returned from the function are checked and an appropriate content-type and response is sent back
|
|
52
|
-
to your HTTP call.`)
|
|
53
|
-
|
|
51
|
+
to your HTTP call.`);
|
|
52
|
+
method = Methods.post;
|
|
53
|
+
path = HTTPRoutes.function;
|
|
54
|
+
tags = [APITags.browserAPI];
|
|
55
|
+
handler = async (
|
|
54
56
|
req: Request,
|
|
55
57
|
res: ServerResponse,
|
|
56
58
|
browser: BrowserInstance,
|
|
57
59
|
): Promise<void> => {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
if (!getConfig || !getDebug) {
|
|
61
|
-
throw new ServerError(`Couldn't load configuration for request`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const debug = getDebug();
|
|
65
|
-
const config = getConfig();
|
|
60
|
+
const debug = this.debug();
|
|
61
|
+
const config = this.config();
|
|
66
62
|
const handler = functionHandler(config, debug);
|
|
67
63
|
const { contentType, payload, page } = await handler(req, browser);
|
|
68
64
|
|
|
@@ -89,10 +85,5 @@ const route: BrowserHTTPRoute = {
|
|
|
89
85
|
}
|
|
90
86
|
|
|
91
87
|
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
path: HTTPRoutes.function,
|
|
95
|
-
tags: [APITags.browserAPI],
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
export default route;
|
|
88
|
+
};
|
|
89
|
+
}
|