@browserless.io/browserless 2.2.0 → 2.3.0-beta-2
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/README.md +2 -2
- package/bin/browserless.js +67 -27
- package/bin/scaffold/README.md +50 -0
- package/bin/scaffold/src/hello-world.http.ts +2 -1
- package/build/browserless.d.ts +3 -0
- package/build/browserless.js +43 -19
- package/build/browsers/chrome.cdp.js +1 -1
- package/build/browsers/chrome.playwright.js +1 -1
- package/build/browsers/index.d.ts +1 -0
- package/build/browsers/index.js +4 -1
- package/build/data/classes.json +1 -1
- package/build/data/selectors.json +1 -1
- package/build/exports.js +1 -2
- package/build/limiter.d.ts +2 -1
- package/build/limiter.js +6 -3
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/content.post.d.ts +2 -1
- package/build/routes/chrome/http/content.post.js +3 -2
- package/build/routes/chrome/http/download.post.d.ts +2 -1
- package/build/routes/chrome/http/download.post.js +3 -2
- package/build/routes/chrome/http/function.post.d.ts +2 -1
- package/build/routes/chrome/http/function.post.js +3 -2
- package/build/routes/chrome/http/json-list.get.d.ts +5 -1
- package/build/routes/chrome/http/json-list.get.js +5 -1
- package/build/routes/chrome/http/json-new.put.d.ts +5 -1
- package/build/routes/chrome/http/json-new.put.js +5 -1
- package/build/routes/chrome/http/json-protocol.get.d.ts +5 -1
- package/build/routes/chrome/http/json-protocol.get.js +5 -1
- package/build/routes/chrome/http/json-version.get.d.ts +5 -1
- package/build/routes/chrome/http/json-version.get.js +5 -1
- package/build/routes/chrome/http/pdf.post.body.json +8 -8
- package/build/routes/chrome/http/pdf.post.d.ts +2 -1
- package/build/routes/chrome/http/pdf.post.js +3 -2
- package/build/routes/chrome/http/performance.post.d.ts +2 -1
- package/build/routes/chrome/http/performance.post.js +3 -2
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/scrape.post.d.ts +2 -1
- package/build/routes/chrome/http/scrape.post.js +3 -2
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.d.ts +2 -1
- package/build/routes/chrome/http/screenshot.post.js +3 -2
- package/build/routes/chrome/ws/browser.d.ts +2 -1
- package/build/routes/chrome/ws/browser.js +3 -2
- package/build/routes/chrome/ws/cdp.d.ts +2 -1
- package/build/routes/chrome/ws/cdp.js +3 -2
- package/build/routes/chrome/ws/page.d.ts +3 -2
- package/build/routes/chrome/ws/page.js +3 -2
- package/build/routes/chrome/ws/playwright.d.ts +1 -0
- package/build/routes/chrome/ws/playwright.js +2 -1
- 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/firefox/ws/playwright.d.ts +7 -2
- package/build/routes/firefox/ws/playwright.js +3 -2
- package/build/routes/firefox/ws/playwright.query.json +29 -31
- package/build/routes/management/http/config.get.d.ts +1 -0
- package/build/routes/management/http/config.get.js +2 -1
- package/build/routes/management/http/metrics-total.get.d.ts +1 -0
- package/build/routes/management/http/metrics-total.get.js +2 -1
- package/build/routes/management/http/metrics.get.d.ts +1 -0
- package/build/routes/management/http/metrics.get.js +2 -1
- package/build/routes/management/http/sessions.get.d.ts +1 -0
- package/build/routes/management/http/sessions.get.js +2 -1
- package/build/routes/management/http/static.get.d.ts +1 -0
- package/build/routes/management/http/static.get.js +8 -7
- package/build/routes/webkit/ws/playwright.d.ts +2 -1
- package/build/routes/webkit/ws/playwright.js +3 -2
- package/build/shared/browser.ws.d.ts +2 -1
- package/build/shared/browser.ws.js +3 -2
- package/build/shared/chromium.playwright.ws.d.ts +2 -1
- package/build/shared/chromium.playwright.ws.js +3 -2
- package/build/shared/chromium.ws.d.ts +2 -1
- package/build/shared/chromium.ws.js +3 -2
- package/build/shared/content.http.d.ts +2 -1
- package/build/shared/content.http.js +3 -2
- package/build/shared/download.http.d.ts +2 -1
- package/build/shared/download.http.js +3 -2
- package/build/shared/function.http.d.ts +2 -1
- package/build/shared/function.http.js +3 -2
- package/build/shared/json-list.http.d.ts +2 -1
- package/build/shared/json-list.http.js +3 -2
- package/build/shared/json-new.http.d.ts +2 -1
- package/build/shared/json-new.http.js +3 -2
- package/build/shared/json-protocol.http.d.ts +2 -1
- package/build/shared/json-protocol.http.js +3 -2
- package/build/shared/json-version.http.d.ts +2 -1
- package/build/shared/json-version.http.js +3 -2
- package/build/shared/page.ws.d.ts +2 -1
- package/build/shared/page.ws.js +3 -2
- package/build/shared/pdf.http.d.ts +2 -1
- package/build/shared/pdf.http.js +3 -2
- package/build/shared/performance.http.d.ts +1 -0
- package/build/shared/performance.http.js +2 -1
- package/build/shared/scrape.http.d.ts +2 -1
- package/build/shared/scrape.http.js +3 -2
- package/build/shared/screenshot.http.d.ts +1 -0
- package/build/shared/screenshot.http.js +2 -1
- package/build/shared/utils/performance/main.js +1 -1
- package/build/types.d.ts +91 -0
- package/build/types.js +54 -0
- package/build/utils.d.ts +1 -1
- package/build/utils.js +11 -6
- package/docker/chromium/Dockerfile +1 -2
- package/docker/firefox/Dockerfile +1 -2
- package/docker/multi/Dockerfile +2 -4
- package/docker/webkit/Dockerfile +1 -2
- package/package.json +11 -11
- package/scripts/build-open-api.js +142 -124
- package/src/browserless.ts +60 -22
- package/src/browsers/chrome.cdp.ts +1 -1
- package/src/browsers/chrome.playwright.ts +1 -1
- package/src/browsers/index.ts +5 -1
- package/src/exports.ts +1 -3
- package/src/limiter.ts +7 -3
- package/src/router.ts +6 -2
- package/src/routes/chrome/http/content.post.ts +7 -2
- package/src/routes/chrome/http/download.post.ts +7 -2
- package/src/routes/chrome/http/function.post.ts +7 -2
- package/src/routes/chrome/http/json-list.get.ts +7 -1
- package/src/routes/chrome/http/json-new.put.ts +7 -1
- package/src/routes/chrome/http/json-protocol.get.ts +7 -1
- package/src/routes/chrome/http/json-version.get.ts +7 -1
- package/src/routes/chrome/http/pdf.post.ts +7 -2
- package/src/routes/chrome/http/performance.post.ts +7 -2
- package/src/routes/chrome/http/scrape.post.ts +7 -2
- package/src/routes/chrome/http/screenshot.post.ts +7 -2
- package/src/routes/chrome/ws/browser.ts +3 -2
- package/src/routes/chrome/ws/cdp.ts +7 -2
- package/src/routes/chrome/ws/page.ts +3 -2
- package/src/routes/chrome/ws/playwright.ts +6 -1
- package/src/routes/firefox/ws/playwright.ts +6 -2
- package/src/routes/management/http/config.get.ts +2 -0
- package/src/routes/management/http/metrics-total.get.ts +2 -0
- package/src/routes/management/http/metrics.get.ts +2 -0
- package/src/routes/management/http/sessions.get.ts +2 -0
- package/src/routes/management/http/static.get.ts +16 -9
- package/src/routes/webkit/ws/playwright.ts +3 -1
- package/src/shared/browser.ws.ts +3 -1
- package/src/shared/chromium.playwright.ws.ts +3 -1
- package/src/shared/chromium.ws.ts +3 -1
- package/src/shared/content.http.ts +3 -1
- package/src/shared/download.http.ts +3 -1
- package/src/shared/function.http.ts +3 -1
- package/src/shared/json-list.http.ts +3 -1
- package/src/shared/json-new.http.ts +3 -1
- package/src/shared/json-protocol.http.ts +3 -1
- package/src/shared/json-version.http.ts +3 -1
- package/src/shared/page.ws.ts +3 -1
- package/src/shared/pdf.http.ts +3 -1
- package/src/shared/performance.http.ts +2 -0
- package/src/shared/scrape.http.ts +3 -1
- package/src/shared/screenshot.http.ts +2 -0
- package/src/shared/utils/performance/main.ts +1 -2
- package/src/types.ts +66 -0
- package/src/utils.ts +13 -7
- package/static/docs/swagger.json +91 -49
- package/static/docs/swagger.min.json +6697 -0
- package/static/function/client.js +323 -323
|
@@ -11,6 +11,13 @@ import { marked } from 'marked';
|
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
const moduleMain = import.meta.url.endsWith(process.argv[1]);
|
|
13
13
|
const swaggerJSONPath = join(__dirname, '..', 'static', 'docs', 'swagger.json');
|
|
14
|
+
const swaggerJSONMinimal = join(
|
|
15
|
+
__dirname,
|
|
16
|
+
'..',
|
|
17
|
+
'static',
|
|
18
|
+
'docs',
|
|
19
|
+
'swagger.min.json',
|
|
20
|
+
);
|
|
14
21
|
const packageJSONPath = join(__dirname, '..', 'package.json');
|
|
15
22
|
|
|
16
23
|
const readFileOrNull = async (path) => {
|
|
@@ -42,6 +49,7 @@ const sortSwaggerRequiredAlpha = (prop, otherProp) => {
|
|
|
42
49
|
const buildOpenAPI = async (
|
|
43
50
|
externalHTTPRoutes = [],
|
|
44
51
|
externalWebSocketRoutes = [],
|
|
52
|
+
disabledRoutes = [],
|
|
45
53
|
) => {
|
|
46
54
|
const [{ getRouteFiles }, { Config }, { errorCodes }, packageJSON] =
|
|
47
55
|
await Promise.all([
|
|
@@ -62,8 +70,6 @@ const buildOpenAPI = async (
|
|
|
62
70
|
customSiteTitle: 'Browserless Documentation',
|
|
63
71
|
definitions: {},
|
|
64
72
|
info: {
|
|
65
|
-
// Concatenation necessary for Changelog to show up in sidebar
|
|
66
|
-
description: readme + `\n# Changelog\n` + changelog,
|
|
67
73
|
title: 'Browserless',
|
|
68
74
|
version: JSON.parse(packageJSON.toString()).version,
|
|
69
75
|
'x-logo': {
|
|
@@ -93,6 +99,11 @@ const buildOpenAPI = async (
|
|
|
93
99
|
throw new Error(`Invalid route file to import docs ${routeModule}`);
|
|
94
100
|
}
|
|
95
101
|
const route = new Route();
|
|
102
|
+
|
|
103
|
+
if (disabledRoutes.includes(route.name)) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
const { name } = parse(routeModule);
|
|
97
108
|
const body = routeModule.replace('.js', '.body.json');
|
|
98
109
|
const query = routeModule.replace('.js', '.query.json');
|
|
@@ -100,7 +111,7 @@ const buildOpenAPI = async (
|
|
|
100
111
|
const isWebSocket = routeModule.includes('/ws/') || name.endsWith('ws');
|
|
101
112
|
const path = Array.isArray(route.path)
|
|
102
113
|
? route.path.join(' ')
|
|
103
|
-
:
|
|
114
|
+
: route.path;
|
|
104
115
|
const {
|
|
105
116
|
tags,
|
|
106
117
|
description,
|
|
@@ -130,145 +141,152 @@ const buildOpenAPI = async (
|
|
|
130
141
|
}),
|
|
131
142
|
);
|
|
132
143
|
|
|
133
|
-
const paths = routeMetaData
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
const paths = routeMetaData
|
|
145
|
+
.filter((_) => !!_)
|
|
146
|
+
.reduce((accum, r) => {
|
|
147
|
+
const swaggerRoute = {
|
|
148
|
+
definitions: {},
|
|
149
|
+
description: r.description,
|
|
150
|
+
parameters: [],
|
|
151
|
+
requestBody: {
|
|
152
|
+
content: {},
|
|
153
|
+
},
|
|
154
|
+
responses: {
|
|
155
|
+
...errorCodes,
|
|
156
|
+
},
|
|
157
|
+
summary: r.path,
|
|
158
|
+
tags: r.tags,
|
|
159
|
+
};
|
|
147
160
|
|
|
148
|
-
|
|
161
|
+
r.method = r.isWebSocket ? 'get' : r.method;
|
|
149
162
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
163
|
+
// Find all the swagger definitions and merge them into the
|
|
164
|
+
// definitions object
|
|
165
|
+
const allDefs = {
|
|
166
|
+
...(r?.body?.definitions || {}),
|
|
167
|
+
...(r?.query?.definitions || {}),
|
|
168
|
+
...(r?.response?.definitions || {}),
|
|
169
|
+
};
|
|
157
170
|
|
|
158
|
-
|
|
159
|
-
// @ts-ignore
|
|
160
|
-
swaggerJSON.definitions[defName] =
|
|
171
|
+
Object.entries(allDefs).forEach(([defName, definition]) => {
|
|
161
172
|
// @ts-ignore
|
|
162
|
-
swaggerJSON.definitions[defName]
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
swaggerRoute.responses['101'] = {
|
|
167
|
-
description: 'Indicates successful WebSocket upgrade.',
|
|
168
|
-
};
|
|
169
|
-
}
|
|
173
|
+
swaggerJSON.definitions[defName] =
|
|
174
|
+
// @ts-ignore
|
|
175
|
+
swaggerJSON.definitions[defName] ?? definition;
|
|
176
|
+
});
|
|
170
177
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (r.response) {
|
|
175
|
-
if (r.contentTypes.length === 1) {
|
|
176
|
-
const [type] = r.contentTypes;
|
|
177
|
-
swaggerRoute.responses['200'] = {
|
|
178
|
-
content: {
|
|
179
|
-
[type]: {
|
|
180
|
-
schema: r.response,
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
description: r.response.description,
|
|
178
|
+
if (r.isWebSocket) {
|
|
179
|
+
swaggerRoute.responses['101'] = {
|
|
180
|
+
description: 'Indicates successful WebSocket upgrade.',
|
|
184
181
|
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Does a best-attempt at configuring multiple response types
|
|
185
|
+
// Won't figure out APIs that return mixed response types like
|
|
186
|
+
// JSON and binary blobs
|
|
187
|
+
if (r.response) {
|
|
188
|
+
if (r.contentTypes.length === 1) {
|
|
189
|
+
const [type] = r.contentTypes;
|
|
190
|
+
swaggerRoute.responses['200'] = {
|
|
191
|
+
content: {
|
|
192
|
+
[type]: {
|
|
193
|
+
schema: r.response,
|
|
192
194
|
},
|
|
193
|
-
}
|
|
194
|
-
return accum;
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
content: {},
|
|
195
|
+
},
|
|
198
196
|
description: r.response.description,
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
};
|
|
198
|
+
} else {
|
|
199
|
+
const okResponses = r.contentTypes.reduce(
|
|
200
|
+
(accum, c) => {
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
accum.content[c] = {
|
|
203
|
+
schema: {
|
|
204
|
+
type: 'text',
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
return accum;
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
content: {},
|
|
211
|
+
description: r.response.description,
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
swaggerRoute.responses['200'] = okResponses;
|
|
215
|
+
}
|
|
202
216
|
}
|
|
203
|
-
}
|
|
204
217
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
218
|
+
// Does a best-attempt at configuring multiple body types and
|
|
219
|
+
// ignores the "accepts" properties on routes since we can't
|
|
220
|
+
// yet correlate the accepted types to the proper body
|
|
221
|
+
if (r.body) {
|
|
222
|
+
const { properties, type, anyOf } = r.body;
|
|
223
|
+
if (anyOf) {
|
|
224
|
+
// @ts-ignore
|
|
225
|
+
anyOf.forEach((anyType) => {
|
|
226
|
+
if (anyType.type === 'string') {
|
|
227
|
+
const type = r.accepts.filter(
|
|
228
|
+
// @ts-ignore
|
|
229
|
+
(accept) => accept !== 'application/json',
|
|
230
|
+
);
|
|
231
|
+
swaggerRoute.requestBody.content[type] = {
|
|
232
|
+
schema: {
|
|
233
|
+
type: 'string',
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
224
237
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
if (anyType['$ref']) {
|
|
239
|
+
swaggerRoute.requestBody.content['application/json'] = {
|
|
240
|
+
schema: {
|
|
241
|
+
$ref: anyType['$ref'],
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
234
247
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
248
|
+
// Handle JSON
|
|
249
|
+
if (type === 'object') {
|
|
250
|
+
swaggerRoute.requestBody.content['application/json'] = {
|
|
251
|
+
schema: {
|
|
252
|
+
properties,
|
|
253
|
+
type: 'object',
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
243
257
|
}
|
|
244
|
-
}
|
|
245
258
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
259
|
+
// Queries are easy in comparison, but still have to be iterated
|
|
260
|
+
// over and made open-api-able
|
|
261
|
+
if (r.query) {
|
|
262
|
+
const { properties, required } = r.query;
|
|
263
|
+
const props = Object.keys(properties || {});
|
|
264
|
+
if (props.length) {
|
|
265
|
+
swaggerRoute.parameters = props
|
|
266
|
+
.map((prop) => ({
|
|
267
|
+
in: 'query',
|
|
268
|
+
name: prop,
|
|
269
|
+
required: required?.includes(prop),
|
|
270
|
+
schema: properties[prop],
|
|
271
|
+
}))
|
|
272
|
+
.sort(sortSwaggerRequiredAlpha);
|
|
273
|
+
}
|
|
260
274
|
}
|
|
261
|
-
}
|
|
262
275
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
276
|
+
// @ts-ignore
|
|
277
|
+
accum[r.path] = accum[r.path] || {};
|
|
278
|
+
// @ts-ignore
|
|
279
|
+
accum[r.path][r.method] = swaggerRoute;
|
|
267
280
|
|
|
268
|
-
|
|
269
|
-
|
|
281
|
+
return accum;
|
|
282
|
+
}, {});
|
|
270
283
|
swaggerJSON.paths = paths;
|
|
271
|
-
fs.writeFile(
|
|
284
|
+
await fs.writeFile(
|
|
285
|
+
swaggerJSONMinimal,
|
|
286
|
+
JSON.stringify(swaggerJSON, null, ' '),
|
|
287
|
+
);
|
|
288
|
+
swaggerJSON.info.description = readme + `\n# Changelog\n` + changelog;
|
|
289
|
+
await fs.writeFile(swaggerJSONPath, JSON.stringify(swaggerJSON, null, ' '));
|
|
272
290
|
};
|
|
273
291
|
|
|
274
292
|
export default buildOpenAPI;
|
package/src/browserless.ts
CHANGED
|
@@ -36,6 +36,12 @@ type Implements<T> = {
|
|
|
36
36
|
new (...args: unknown[]): T;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
type routeInstances =
|
|
40
|
+
| HTTPRoute
|
|
41
|
+
| BrowserHTTPRoute
|
|
42
|
+
| WebSocketRoute
|
|
43
|
+
| BrowserWebsocketRoute;
|
|
44
|
+
|
|
39
45
|
export class Browserless {
|
|
40
46
|
protected debug: debug.Debugger = createLogger('index');
|
|
41
47
|
protected browserManager: BrowserManager;
|
|
@@ -48,6 +54,7 @@ export class Browserless {
|
|
|
48
54
|
protected token: Token;
|
|
49
55
|
protected webhooks: WebHooks;
|
|
50
56
|
|
|
57
|
+
disabledRouteNames: string[] = [];
|
|
51
58
|
webSocketRouteFiles: string[] = [];
|
|
52
59
|
httpRouteFiles: string[] = [];
|
|
53
60
|
server?: HTTPServer;
|
|
@@ -138,6 +145,14 @@ export class Browserless {
|
|
|
138
145
|
);
|
|
139
146
|
};
|
|
140
147
|
|
|
148
|
+
private routeIsDisabled(route: routeInstances) {
|
|
149
|
+
return this.disabledRouteNames.some((name) => name === route.name);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public disableRoutes(...routeNames: string[]) {
|
|
153
|
+
this.disabledRouteNames.push(...routeNames);
|
|
154
|
+
}
|
|
155
|
+
|
|
141
156
|
public addHTTPRoute(httpRouteFilePath: string) {
|
|
142
157
|
this.httpRouteFiles.push(httpRouteFilePath);
|
|
143
158
|
}
|
|
@@ -171,7 +186,7 @@ export class Browserless {
|
|
|
171
186
|
WebkitPlaywright,
|
|
172
187
|
];
|
|
173
188
|
|
|
174
|
-
const [[
|
|
189
|
+
const [[internalHttpRouteFiles, internalWsRouteFiles], installedBrowsers] =
|
|
175
190
|
await Promise.all([getRouteFiles(this.config), availableBrowsers]);
|
|
176
191
|
|
|
177
192
|
const docsLink = makeExternalURL(this.config.getExternalAddress(), '/docs');
|
|
@@ -180,7 +195,10 @@ export class Browserless {
|
|
|
180
195
|
this.debug(`Running as user "${userInfo().username}"`);
|
|
181
196
|
this.debug('Starting import of HTTP Routes');
|
|
182
197
|
|
|
183
|
-
for (const httpRoute of [
|
|
198
|
+
for (const httpRoute of [
|
|
199
|
+
...internalHttpRouteFiles,
|
|
200
|
+
...this.httpRouteFiles,
|
|
201
|
+
]) {
|
|
184
202
|
if (httpRoute.endsWith('js')) {
|
|
185
203
|
const { name } = path.parse(httpRoute);
|
|
186
204
|
const [bodySchema, querySchema] = await Promise.all(
|
|
@@ -209,20 +227,26 @@ export class Browserless {
|
|
|
209
227
|
this.metrics,
|
|
210
228
|
this.monitoring,
|
|
211
229
|
);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
230
|
+
|
|
231
|
+
if (!this.routeIsDisabled(route)) {
|
|
232
|
+
route.bodySchema = safeParse(bodySchema);
|
|
233
|
+
route.querySchema = safeParse(querySchema);
|
|
234
|
+
route.config = () => this.config;
|
|
235
|
+
route.metrics = () => this.metrics;
|
|
236
|
+
route.monitoring = () => this.monitoring;
|
|
237
|
+
route.fileSystem = () => this.fileSystem;
|
|
238
|
+
route.debug = () => logger;
|
|
239
|
+
|
|
240
|
+
httpRoutes.push(route);
|
|
241
|
+
}
|
|
221
242
|
}
|
|
222
243
|
}
|
|
223
244
|
|
|
224
245
|
this.debug('Starting import of WebSocket Routes');
|
|
225
|
-
for (const wsRoute of [
|
|
246
|
+
for (const wsRoute of [
|
|
247
|
+
...internalWsRouteFiles,
|
|
248
|
+
...this.webSocketRouteFiles,
|
|
249
|
+
]) {
|
|
226
250
|
if (wsRoute.endsWith('js')) {
|
|
227
251
|
const { name } = path.parse(wsRoute);
|
|
228
252
|
const [, querySchema] = await Promise.all(
|
|
@@ -246,7 +270,6 @@ export class Browserless {
|
|
|
246
270
|
| Implements<WebSocketRoute>
|
|
247
271
|
| Implements<BrowserWebsocketRoute>;
|
|
248
272
|
} = await import(wsImport + `?cb=${Date.now()}`);
|
|
249
|
-
|
|
250
273
|
const route = new Route(
|
|
251
274
|
this.browserManager,
|
|
252
275
|
this.config,
|
|
@@ -255,19 +278,23 @@ export class Browserless {
|
|
|
255
278
|
this.metrics,
|
|
256
279
|
this.monitoring,
|
|
257
280
|
);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
281
|
+
|
|
282
|
+
if (!this.routeIsDisabled(route)) {
|
|
283
|
+
route.querySchema = safeParse(querySchema);
|
|
284
|
+
route.config = () => this.config;
|
|
285
|
+
route.metrics = () => this.metrics;
|
|
286
|
+
route.monitoring = () => this.monitoring;
|
|
287
|
+
route.fileSystem = () => this.fileSystem;
|
|
288
|
+
route.debug = () => logger;
|
|
289
|
+
|
|
290
|
+
wsRoutes.push(route);
|
|
291
|
+
}
|
|
266
292
|
}
|
|
267
293
|
}
|
|
268
294
|
|
|
295
|
+
const allRoutes = [...httpRoutes, ...wsRoutes];
|
|
269
296
|
// Validate that we have the browsers they are asking for
|
|
270
|
-
|
|
297
|
+
allRoutes.forEach((route) => {
|
|
271
298
|
if (
|
|
272
299
|
'browser' in route &&
|
|
273
300
|
route.browser &&
|
|
@@ -280,6 +307,17 @@ export class Browserless {
|
|
|
280
307
|
}
|
|
281
308
|
});
|
|
282
309
|
|
|
310
|
+
const duplicateNamedRoutes = allRoutes
|
|
311
|
+
.filter((e, i, a) => a.findIndex((r) => r.name === e.name) !== i)
|
|
312
|
+
.map((r) => r.name);
|
|
313
|
+
|
|
314
|
+
if (duplicateNamedRoutes.length) {
|
|
315
|
+
this.debug(
|
|
316
|
+
`Found duplicate routing names. Route names must be unique:`,
|
|
317
|
+
duplicateNamedRoutes,
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
283
321
|
httpRoutes.forEach((r) => this.router.registerHTTPRoute(r));
|
|
284
322
|
wsRoutes.forEach((r) => this.router.registerWebSocketRoute(r));
|
|
285
323
|
|
|
@@ -5,6 +5,6 @@ import {
|
|
|
5
5
|
import { ChromiumCDP } from './chromium.cdp.js';
|
|
6
6
|
|
|
7
7
|
export class ChromeCDP extends ChromiumCDP {
|
|
8
|
-
protected executablePath = chromeExecutablePath;
|
|
8
|
+
protected executablePath = chromeExecutablePath();
|
|
9
9
|
protected debug = createLogger('browsers:chrome:cdp');
|
|
10
10
|
}
|
|
@@ -5,6 +5,6 @@ import {
|
|
|
5
5
|
import { ChromiumPlaywright } from './chromium.playwright.js';
|
|
6
6
|
|
|
7
7
|
export class ChromePlaywright extends ChromiumPlaywright {
|
|
8
|
-
protected executablePath = chromeExecutablePath;
|
|
8
|
+
protected executablePath = chromeExecutablePath();
|
|
9
9
|
protected debug = createLogger('browsers:chrome:playwright');
|
|
10
10
|
}
|
package/src/browsers/index.ts
CHANGED
|
@@ -63,6 +63,10 @@ export class BrowserManager {
|
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
+
protected onNewPage = async (req: Request, page: unknown) => {
|
|
67
|
+
await pageHook({ meta: req.parsed, page });
|
|
68
|
+
};
|
|
69
|
+
|
|
66
70
|
/**
|
|
67
71
|
* Returns the /json/protocol API contents from Chromium or Chrome, whichever is installed,
|
|
68
72
|
* and modifies URLs to set them to the appropriate addresses configured.
|
|
@@ -466,7 +470,7 @@ export class BrowserManager {
|
|
|
466
470
|
await browserHook({ browser, meta: req.parsed });
|
|
467
471
|
|
|
468
472
|
browser.on('newPage', async (page) => {
|
|
469
|
-
await
|
|
473
|
+
await this.onNewPage(req, page);
|
|
470
474
|
(router.onNewPage || noop)(req.parsed || '', page);
|
|
471
475
|
});
|
|
472
476
|
|
package/src/exports.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Export all them goods...
|
|
2
2
|
export * from './browserless.js';
|
|
3
3
|
export * from './config.js';
|
|
4
4
|
export * from './constants.js';
|
|
@@ -16,8 +16,6 @@ export * from './token.js';
|
|
|
16
16
|
export * from './types.js';
|
|
17
17
|
export * from './utils.js';
|
|
18
18
|
export * from './webhooks.js';
|
|
19
|
-
|
|
20
|
-
// Browsers and Helpers
|
|
21
19
|
export * from './browsers/index.js';
|
|
22
20
|
export * from './browsers/chrome.cdp.js';
|
|
23
21
|
export * from './browsers/chrome.playwright.js';
|
package/src/limiter.ts
CHANGED
|
@@ -77,6 +77,10 @@ export class Limiter extends q {
|
|
|
77
77
|
this.logQueue('All jobs complete.');
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
protected jobEnd(jobInfo: AfterResponse) {
|
|
81
|
+
afterRequest(jobInfo);
|
|
82
|
+
}
|
|
83
|
+
|
|
80
84
|
protected handleSuccess({ detail: { job } }: { detail: { job: Job } }) {
|
|
81
85
|
const timeUsed = Date.now() - job.start;
|
|
82
86
|
this.debug(
|
|
@@ -84,7 +88,7 @@ export class Limiter extends q {
|
|
|
84
88
|
);
|
|
85
89
|
this.metrics.addSuccessful(Date.now() - job.start);
|
|
86
90
|
// @TODO Figure out a better argument handling for jobs
|
|
87
|
-
|
|
91
|
+
this.jobEnd({
|
|
88
92
|
req: job.args[0],
|
|
89
93
|
start: job.start,
|
|
90
94
|
status: 'successful',
|
|
@@ -104,7 +108,7 @@ export class Limiter extends q {
|
|
|
104
108
|
this.webhooks.callTimeoutAlertURL();
|
|
105
109
|
this.debug(`Calling timeout handler`);
|
|
106
110
|
job?.onTimeoutFn(job);
|
|
107
|
-
|
|
111
|
+
this.jobEnd({
|
|
108
112
|
req: job.args[0],
|
|
109
113
|
start: job.start,
|
|
110
114
|
status: 'timedout',
|
|
@@ -121,7 +125,7 @@ export class Limiter extends q {
|
|
|
121
125
|
this.debug(`Recording failed stat, cleaning up: "${error?.toString()}"`);
|
|
122
126
|
this.metrics.addError(Date.now() - job.start);
|
|
123
127
|
this.webhooks.callErrorAlertURL(error?.toString() ?? 'Unknown Error');
|
|
124
|
-
|
|
128
|
+
this.jobEnd({
|
|
125
129
|
req: job.args[0],
|
|
126
130
|
start: job.start,
|
|
127
131
|
status: 'error',
|
package/src/router.ts
CHANGED
|
@@ -166,7 +166,9 @@ export class Router {
|
|
|
166
166
|
: wrapped;
|
|
167
167
|
route.path = Array.isArray(route.path) ? route.path : [route.path];
|
|
168
168
|
const registeredPaths = this.httpRoutes.map((r) => r.path).flat();
|
|
169
|
-
const duplicatePaths = registeredPaths.filter((path) =>
|
|
169
|
+
const duplicatePaths = registeredPaths.filter((path) =>
|
|
170
|
+
route.path.includes(path),
|
|
171
|
+
);
|
|
170
172
|
|
|
171
173
|
if (duplicatePaths.length) {
|
|
172
174
|
this.log(`Found duplicate routes: ${duplicatePaths.join(', ')}`);
|
|
@@ -194,7 +196,9 @@ export class Router {
|
|
|
194
196
|
: wrapped;
|
|
195
197
|
route.path = Array.isArray(route.path) ? route.path : [route.path];
|
|
196
198
|
const registeredPaths = this.webSocketRoutes.map((r) => r.path).flat();
|
|
197
|
-
const duplicatePaths = registeredPaths.filter((path) =>
|
|
199
|
+
const duplicatePaths = registeredPaths.filter((path) =>
|
|
200
|
+
route.path.includes(path),
|
|
201
|
+
);
|
|
198
202
|
|
|
199
203
|
if (duplicatePaths.length) {
|
|
200
204
|
this.log(`Found duplicate routes: ${duplicatePaths.join(', ')}`);
|
|
@@ -4,9 +4,14 @@ import {
|
|
|
4
4
|
QuerySchema,
|
|
5
5
|
ResponseSchema,
|
|
6
6
|
} from '../../../shared/content.http.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
BrowserlessRoutes,
|
|
9
|
+
ChromeCDP,
|
|
10
|
+
HTTPRoutes,
|
|
11
|
+
} from '@browserless.io/browserless';
|
|
8
12
|
|
|
9
|
-
export default class
|
|
13
|
+
export default class ChromeContentPostRoute extends Content {
|
|
14
|
+
name = BrowserlessRoutes.ChromeContentPostRoute;
|
|
10
15
|
browser = ChromeCDP;
|
|
11
16
|
path = [HTTPRoutes.chromeContent];
|
|
12
17
|
}
|
|
@@ -4,9 +4,14 @@ import {
|
|
|
4
4
|
QuerySchema,
|
|
5
5
|
ResponseSchema,
|
|
6
6
|
} from '../../../shared/download.http.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
BrowserlessRoutes,
|
|
9
|
+
ChromeCDP,
|
|
10
|
+
HTTPRoutes,
|
|
11
|
+
} from '@browserless.io/browserless';
|
|
8
12
|
|
|
9
|
-
export default class
|
|
13
|
+
export default class ChromeDownloadPostRoute extends Download {
|
|
14
|
+
name = BrowserlessRoutes.ChromeDownloadPostRoute;
|
|
10
15
|
browser = ChromeCDP;
|
|
11
16
|
path = [HTTPRoutes.chromeDownload];
|
|
12
17
|
}
|
|
@@ -4,9 +4,14 @@ import {
|
|
|
4
4
|
QuerySchema,
|
|
5
5
|
ResponseSchema,
|
|
6
6
|
} from '../../../shared/function.http.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
BrowserlessRoutes,
|
|
9
|
+
ChromeCDP,
|
|
10
|
+
HTTPRoutes,
|
|
11
|
+
} from '@browserless.io/browserless';
|
|
8
12
|
|
|
9
|
-
export default class
|
|
13
|
+
export default class ChromeFunctionPostRoute extends Function {
|
|
14
|
+
name = BrowserlessRoutes.ChromeFunctionPostRoute;
|
|
10
15
|
browser = ChromeCDP;
|
|
11
16
|
path = [HTTPRoutes.chromeFunction];
|
|
12
17
|
}
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { BrowserlessRoutes } from '@browserless.io/browserless';
|
|
2
|
+
export { ResponseSchema } from '../../../shared/json-list.http.js';
|
|
3
|
+
import { default as ChromiumJSONListGetRoute } from '../../../shared/json-list.http.js';
|
|
4
|
+
|
|
5
|
+
export default class ChromeJSONListGetRoute extends ChromiumJSONListGetRoute {
|
|
6
|
+
name = BrowserlessRoutes.ChromeJSONListGetRoute;
|
|
7
|
+
}
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { BrowserlessRoutes } from '@browserless.io/browserless';
|
|
2
|
+
export { ResponseSchema } from '../../../shared/json-new.http.js';
|
|
3
|
+
import { default as ChromiumJSONNewPutRoute } from '../../../shared/json-new.http.js';
|
|
4
|
+
|
|
5
|
+
export default class ChromeJSONNewPutRoute extends ChromiumJSONNewPutRoute {
|
|
6
|
+
name = BrowserlessRoutes.ChromeJSONNewPutRoute;
|
|
7
|
+
}
|