@browserless.io/browserless 2.4.0-beta-3 → 2.5.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 +15 -1
- package/bin/browserless.js +17 -127
- package/bin/scaffold/README.md +95 -14
- package/build/browserless.d.ts +4 -2
- package/build/browserless.js +11 -8
- package/build/browsers/index.d.ts +3 -2
- package/build/browsers/index.js +6 -4
- package/build/data/selectors.json +1 -1
- package/build/exports.d.ts +1 -0
- package/build/exports.js +1 -0
- package/build/file-system.d.ts +2 -3
- package/build/file-system.js +10 -21
- package/build/file-system.spec.js +35 -18
- package/build/hooks.d.ts +9 -4
- package/build/hooks.js +26 -8
- package/build/limiter.d.ts +3 -2
- package/build/limiter.js +5 -3
- package/build/limiter.spec.js +45 -26
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/pdf.post.body.json +8 -11
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chromium/http/content.post.body.json +8 -8
- package/build/routes/chromium/http/pdf.post.body.json +8 -11
- package/build/routes/chromium/http/scrape.post.body.json +8 -8
- package/build/routes/chromium/http/screenshot.post.body.json +8 -8
- package/build/routes/management/http/metrics-total.get.js +1 -1
- package/build/routes/management/http/metrics.get.js +2 -2
- package/build/sdk-utils.d.ts +13 -0
- package/build/sdk-utils.js +94 -0
- package/build/server.d.ts +3 -2
- package/build/server.js +6 -4
- package/build/shared/content.http.js +1 -1
- package/build/shared/pdf.http.d.ts +0 -1
- package/build/shared/pdf.http.js +1 -1
- package/build/shared/screenshot.http.js +1 -1
- package/build/types.d.ts +3 -3
- package/build/utils.js +2 -1
- package/package.json +10 -10
- package/src/browserless.ts +22 -5
- package/src/browsers/index.ts +7 -5
- package/src/exports.ts +1 -0
- package/src/file-system.spec.ts +43 -18
- package/src/file-system.ts +16 -30
- package/src/hooks.ts +32 -8
- package/src/limiter.spec.ts +82 -112
- package/src/limiter.ts +3 -3
- package/src/routes/management/http/metrics-total.get.ts +3 -3
- package/src/routes/management/http/metrics.get.ts +2 -2
- package/src/sdk-utils.ts +136 -0
- package/src/server.ts +4 -3
- package/src/shared/content.http.ts +4 -4
- package/src/shared/pdf.http.ts +4 -5
- package/src/shared/screenshot.http.ts +4 -4
- package/src/utils.ts +2 -1
- package/static/docs/swagger.json +11 -17
- package/static/docs/swagger.min.json +10 -16
- package/static/function/client.js +76 -63
package/src/limiter.spec.ts
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Config,
|
|
3
|
+
Hooks,
|
|
3
4
|
Limiter,
|
|
4
5
|
Metrics,
|
|
5
6
|
Monitoring,
|
|
6
7
|
WebHooks,
|
|
7
8
|
sleep,
|
|
8
9
|
} from '@browserless.io/browserless';
|
|
10
|
+
import Sinon, { spy } from 'sinon';
|
|
9
11
|
import { expect } from 'chai';
|
|
10
|
-
import { spy } from 'sinon';
|
|
11
12
|
|
|
12
13
|
const asyncNoop = () => Promise.resolve(undefined);
|
|
13
14
|
const noop = () => undefined;
|
|
14
|
-
const webHooks =
|
|
15
|
-
|
|
16
|
-
callFailedHealthURL: spy(),
|
|
17
|
-
callQueueAlertURL: spy(),
|
|
18
|
-
callRejectAlertURL: spy(),
|
|
19
|
-
callTimeoutAlertURL: spy(),
|
|
20
|
-
};
|
|
15
|
+
const webHooks = Sinon.createStubInstance(WebHooks);
|
|
16
|
+
const hooks = Sinon.createStubInstance(Hooks);
|
|
21
17
|
|
|
22
18
|
describe(`Limiter`, () => {
|
|
23
19
|
afterEach(() => {
|
|
@@ -26,9 +22,13 @@ describe(`Limiter`, () => {
|
|
|
26
22
|
webHooks.callRejectAlertURL.resetHistory();
|
|
27
23
|
webHooks.callTimeoutAlertURL.resetHistory();
|
|
28
24
|
webHooks.callErrorAlertURL.resetHistory();
|
|
25
|
+
hooks.before.resetHistory();
|
|
26
|
+
hooks.after.resetHistory();
|
|
27
|
+
hooks.browser.resetHistory();
|
|
28
|
+
hooks.page.resetHistory();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it('limits and queues function calls and calls queue alert urls', async () => {
|
|
31
|
+
it('limits and queues function calls, calls hooks, and calls queue alert urls', async () => {
|
|
32
32
|
return new Promise((resolve, reject) => {
|
|
33
33
|
const config = new Config();
|
|
34
34
|
config.setQueueAlertURL('https://example.com');
|
|
@@ -40,12 +40,7 @@ describe(`Limiter`, () => {
|
|
|
40
40
|
config.setQueued(1);
|
|
41
41
|
config.setTimeout(-1);
|
|
42
42
|
|
|
43
|
-
const limiter = new Limiter(
|
|
44
|
-
config,
|
|
45
|
-
metrics,
|
|
46
|
-
monitoring,
|
|
47
|
-
webHooks as unknown as WebHooks,
|
|
48
|
-
);
|
|
43
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
49
44
|
const handler = spy();
|
|
50
45
|
const job = limiter.limit(handler, asyncNoop, asyncNoop, noop);
|
|
51
46
|
|
|
@@ -56,6 +51,7 @@ describe(`Limiter`, () => {
|
|
|
56
51
|
|
|
57
52
|
limiter.addEventListener('end', () => {
|
|
58
53
|
try {
|
|
54
|
+
expect(hooks.after.called).to.be.true;
|
|
59
55
|
expect(handler.calledTwice).to.be.true;
|
|
60
56
|
expect(webHooks.callQueueAlertURL.calledOnce).to.be.true;
|
|
61
57
|
expect(metrics.get().queued).to.equal(1);
|
|
@@ -69,27 +65,36 @@ describe(`Limiter`, () => {
|
|
|
69
65
|
});
|
|
70
66
|
}).timeout(5000);
|
|
71
67
|
|
|
72
|
-
it('passes through arguments', () =>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
it('passes through arguments', () =>
|
|
69
|
+
new Promise((resolve, reject) => {
|
|
70
|
+
const args = ['one', 'two', 'three'];
|
|
71
|
+
const config = new Config();
|
|
72
|
+
const metrics = new Metrics();
|
|
73
|
+
const monitoring = new Monitoring(config);
|
|
74
|
+
config.setConcurrent(1);
|
|
75
|
+
config.setQueued(0);
|
|
76
|
+
config.setTimeout(-1);
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
79
|
+
const handler = spy();
|
|
80
|
+
const job = limiter.limit(handler, asyncNoop, asyncNoop, noop);
|
|
81
|
+
// @ts-ignore will fix later
|
|
82
|
+
job(...args);
|
|
83
|
+
expect(handler.args[0]).to.eql(args);
|
|
84
|
+
|
|
85
|
+
limiter.addEventListener('end', () => {
|
|
86
|
+
try {
|
|
87
|
+
expect(hooks.after.args[0][0]).to.have.property('start');
|
|
88
|
+
expect(hooks.after.args[0][0]).to.have.property(
|
|
89
|
+
'status',
|
|
90
|
+
'successful',
|
|
91
|
+
);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
return reject(e);
|
|
94
|
+
}
|
|
95
|
+
resolve(undefined);
|
|
96
|
+
});
|
|
97
|
+
}));
|
|
93
98
|
|
|
94
99
|
it('waits to run jobs until the first are done', async () => {
|
|
95
100
|
const config = new Config();
|
|
@@ -99,12 +104,7 @@ describe(`Limiter`, () => {
|
|
|
99
104
|
config.setQueued(1);
|
|
100
105
|
config.setTimeout(-1);
|
|
101
106
|
|
|
102
|
-
const limiter = new Limiter(
|
|
103
|
-
config,
|
|
104
|
-
metrics,
|
|
105
|
-
monitoring,
|
|
106
|
-
webHooks as unknown as WebHooks,
|
|
107
|
-
);
|
|
107
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
108
108
|
const handlerOne = () => new Promise((r) => setTimeout(r, 50));
|
|
109
109
|
const handlerTwo = spy();
|
|
110
110
|
|
|
@@ -129,12 +129,7 @@ describe(`Limiter`, () => {
|
|
|
129
129
|
config.setQueued(1);
|
|
130
130
|
config.setTimeout(-1);
|
|
131
131
|
|
|
132
|
-
const limiter = new Limiter(
|
|
133
|
-
config,
|
|
134
|
-
metrics,
|
|
135
|
-
monitoring,
|
|
136
|
-
webHooks as unknown as WebHooks,
|
|
137
|
-
);
|
|
132
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
138
133
|
const spy = () => new Promise((_r, rej) => rej(error));
|
|
139
134
|
|
|
140
135
|
const job = limiter.limit(spy, asyncNoop, asyncNoop, noop);
|
|
@@ -150,6 +145,7 @@ describe(`Limiter`, () => {
|
|
|
150
145
|
error,
|
|
151
146
|
);
|
|
152
147
|
expect(webHooks.callErrorAlertURL.calledOnce).to.be.true;
|
|
148
|
+
expect(hooks.after.args[0][0]).to.have.property('status', 'error');
|
|
153
149
|
});
|
|
154
150
|
});
|
|
155
151
|
});
|
|
@@ -163,12 +159,7 @@ describe(`Limiter`, () => {
|
|
|
163
159
|
config.setQueued(0);
|
|
164
160
|
config.setTimeout(-1);
|
|
165
161
|
|
|
166
|
-
const limiter = new Limiter(
|
|
167
|
-
config,
|
|
168
|
-
metrics,
|
|
169
|
-
monitoring,
|
|
170
|
-
webHooks as unknown as WebHooks,
|
|
171
|
-
);
|
|
162
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
172
163
|
|
|
173
164
|
const handler = spy();
|
|
174
165
|
const onError = spy();
|
|
@@ -184,37 +175,41 @@ describe(`Limiter`, () => {
|
|
|
184
175
|
expect(onError.args[0]).to.eql(args);
|
|
185
176
|
});
|
|
186
177
|
|
|
187
|
-
it('calls a timeout handler with arguments if a job takes too long', (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
let timer: NodeJS.Timer;
|
|
197
|
-
const limiter = new Limiter(
|
|
198
|
-
config,
|
|
199
|
-
metrics,
|
|
200
|
-
monitoring,
|
|
201
|
-
webHooks as unknown as WebHooks,
|
|
202
|
-
);
|
|
203
|
-
const handler = () =>
|
|
204
|
-
new Promise((d) => (timer = global.setTimeout(d, 1000)));
|
|
205
|
-
|
|
206
|
-
const onTimeout = (...calledArgs: unknown[]) => {
|
|
207
|
-
clearTimeout(timer as unknown as number);
|
|
208
|
-
expect(calledArgs).to.eql(args);
|
|
209
|
-
expect(webHooks.callTimeoutAlertURL.calledOnce).to.be.true;
|
|
210
|
-
r(null);
|
|
211
|
-
};
|
|
178
|
+
it('calls a timeout handler with arguments if a job takes too long', () =>
|
|
179
|
+
new Promise((resolve, reject) => {
|
|
180
|
+
const args = ['one', 'two', 'three'];
|
|
181
|
+
const config = new Config();
|
|
182
|
+
const metrics = new Metrics();
|
|
183
|
+
const monitoring = new Monitoring(config);
|
|
184
|
+
config.setConcurrent(1);
|
|
185
|
+
config.setQueued(0);
|
|
186
|
+
config.setTimeout(10);
|
|
212
187
|
|
|
213
|
-
|
|
188
|
+
let timer: NodeJS.Timer;
|
|
189
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
190
|
+
const handler = () =>
|
|
191
|
+
new Promise((d) => (timer = global.setTimeout(d, 1000)));
|
|
214
192
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
193
|
+
const onTimeout = (...calledArgs: unknown[]) => {
|
|
194
|
+
clearTimeout(timer as unknown as number);
|
|
195
|
+
expect(calledArgs).to.eql(args);
|
|
196
|
+
expect(webHooks.callTimeoutAlertURL.calledOnce).to.be.true;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const job = limiter.limit(handler, noop, onTimeout, noop);
|
|
200
|
+
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
job(...args);
|
|
203
|
+
|
|
204
|
+
limiter.addEventListener('end', () => {
|
|
205
|
+
try {
|
|
206
|
+
expect(hooks.after.args[0][0]).to.have.property('status', 'timedout');
|
|
207
|
+
} catch (e) {
|
|
208
|
+
return reject(e);
|
|
209
|
+
}
|
|
210
|
+
resolve(undefined);
|
|
211
|
+
});
|
|
212
|
+
}));
|
|
218
213
|
|
|
219
214
|
it('allows overriding the timeouts', async () => {
|
|
220
215
|
const config = new Config();
|
|
@@ -224,12 +219,7 @@ describe(`Limiter`, () => {
|
|
|
224
219
|
config.setQueued(0);
|
|
225
220
|
config.setTimeout(1);
|
|
226
221
|
|
|
227
|
-
const limiter = new Limiter(
|
|
228
|
-
config,
|
|
229
|
-
metrics,
|
|
230
|
-
monitoring,
|
|
231
|
-
webHooks as unknown as WebHooks,
|
|
232
|
-
);
|
|
222
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
233
223
|
const onTimeout = spy();
|
|
234
224
|
const handler = async () => new Promise((r) => setTimeout(r, 10));
|
|
235
225
|
|
|
@@ -252,12 +242,7 @@ describe(`Limiter`, () => {
|
|
|
252
242
|
config.setQueued(0);
|
|
253
243
|
config.setTimeout(20);
|
|
254
244
|
|
|
255
|
-
const limiter = new Limiter(
|
|
256
|
-
config,
|
|
257
|
-
metrics,
|
|
258
|
-
monitoring,
|
|
259
|
-
webHooks as unknown as WebHooks,
|
|
260
|
-
);
|
|
245
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
261
246
|
const handler = () => new Promise((r) => setTimeout(r, 1));
|
|
262
247
|
const timeout = spy();
|
|
263
248
|
const job = limiter.limit(handler, noop, timeout, noop);
|
|
@@ -276,12 +261,7 @@ describe(`Limiter`, () => {
|
|
|
276
261
|
config.setQueued(0);
|
|
277
262
|
config.setTimeout(-1);
|
|
278
263
|
|
|
279
|
-
const limiter = new Limiter(
|
|
280
|
-
config,
|
|
281
|
-
metrics,
|
|
282
|
-
monitoring,
|
|
283
|
-
webHooks as unknown as WebHooks,
|
|
284
|
-
);
|
|
264
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
285
265
|
|
|
286
266
|
const handler = spy();
|
|
287
267
|
const job = limiter.limit(handler, noop, noop, noop);
|
|
@@ -312,12 +292,7 @@ describe(`Limiter`, () => {
|
|
|
312
292
|
config.setQueued(10);
|
|
313
293
|
config.setTimeout(-1);
|
|
314
294
|
|
|
315
|
-
const limiter = new Limiter(
|
|
316
|
-
config,
|
|
317
|
-
metrics,
|
|
318
|
-
monitoring,
|
|
319
|
-
webHooks as unknown as WebHooks,
|
|
320
|
-
);
|
|
295
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
321
296
|
|
|
322
297
|
const handler = spy();
|
|
323
298
|
const job = limiter.limit(handler, noop, noop, noop);
|
|
@@ -343,12 +318,7 @@ describe(`Limiter`, () => {
|
|
|
343
318
|
config.setQueued(10);
|
|
344
319
|
config.setTimeout(-1);
|
|
345
320
|
|
|
346
|
-
const limiter = new Limiter(
|
|
347
|
-
config,
|
|
348
|
-
metrics,
|
|
349
|
-
monitoring,
|
|
350
|
-
webHooks as unknown as WebHooks,
|
|
351
|
-
);
|
|
321
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
352
322
|
|
|
353
323
|
const handler = spy();
|
|
354
324
|
const job = limiter.limit(handler, noop, noop, noop);
|
package/src/limiter.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AfterResponse,
|
|
3
3
|
Config,
|
|
4
|
+
Hooks,
|
|
4
5
|
Metrics,
|
|
5
6
|
Monitoring,
|
|
6
7
|
TooManyRequests,
|
|
7
8
|
WebHooks,
|
|
8
|
-
afterRequest,
|
|
9
9
|
createLogger,
|
|
10
10
|
} from '@browserless.io/browserless';
|
|
11
11
|
import q from 'queue';
|
|
@@ -33,13 +33,13 @@ export class Limiter extends q {
|
|
|
33
33
|
protected metrics: Metrics,
|
|
34
34
|
protected monitor: Monitoring,
|
|
35
35
|
protected webhooks: WebHooks,
|
|
36
|
+
protected hooks: Hooks,
|
|
36
37
|
) {
|
|
37
38
|
super({
|
|
38
39
|
autostart: true,
|
|
39
40
|
concurrency: config.getConcurrent(),
|
|
40
41
|
timeout: config.getTimeout(),
|
|
41
42
|
});
|
|
42
|
-
|
|
43
43
|
this.queued = config.getQueued();
|
|
44
44
|
|
|
45
45
|
this.debug(
|
|
@@ -78,7 +78,7 @@ export class Limiter extends q {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
protected jobEnd(jobInfo: AfterResponse) {
|
|
81
|
-
|
|
81
|
+
this.hooks.after(jobInfo);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
protected handleSuccess({ detail: { job } }: { detail: { job: Job } }) {
|
|
@@ -29,9 +29,9 @@ export default class MetricsTotalGetRoute extends HTTPRoute {
|
|
|
29
29
|
handler = async (_req: Request, res: ServerResponse): Promise<void> => {
|
|
30
30
|
const fileSystem = this.fileSystem();
|
|
31
31
|
const config = this.config();
|
|
32
|
-
const metrics = (
|
|
33
|
-
|
|
34
|
-
);
|
|
32
|
+
const metrics = (
|
|
33
|
+
await fileSystem.read(config.getMetricsJSONPath(), false)
|
|
34
|
+
).map((m) => JSON.parse(m));
|
|
35
35
|
const availableMetrics = metrics.length;
|
|
36
36
|
const totals: IBrowserlessMetricTotals = metrics.reduce(
|
|
37
37
|
(accum, metric) => ({
|
|
@@ -20,7 +20,7 @@ export default class MetricsGetRoute extends HTTPRoute {
|
|
|
20
20
|
browser = null;
|
|
21
21
|
concurrency = false;
|
|
22
22
|
contentTypes = [contentTypes.json];
|
|
23
|
-
description = `
|
|
23
|
+
description = `Returns a list of metric details as far back as possible.`;
|
|
24
24
|
method = Methods.get;
|
|
25
25
|
path = HTTPManagementRoutes.metrics;
|
|
26
26
|
tags = [APITags.management];
|
|
@@ -28,7 +28,7 @@ export default class MetricsGetRoute extends HTTPRoute {
|
|
|
28
28
|
const fileSystem = this.fileSystem();
|
|
29
29
|
const config = this.config();
|
|
30
30
|
|
|
31
|
-
const stats = await fileSystem.read(config.getMetricsJSONPath());
|
|
31
|
+
const stats = await fileSystem.read(config.getMetricsJSONPath(), false);
|
|
32
32
|
const response = `[${stats.join(',')}]`;
|
|
33
33
|
|
|
34
34
|
return writeResponse(res, 200, response, contentTypes.json);
|
package/src/sdk-utils.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { createInterface } from 'readline';
|
|
2
|
+
import debug from 'debug';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
|
|
7
|
+
export const getArgSwitches = () => {
|
|
8
|
+
return process.argv.reduce(
|
|
9
|
+
(accum, arg, idx) => {
|
|
10
|
+
if (!arg.startsWith('--')) {
|
|
11
|
+
return accum;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (arg.includes('=')) {
|
|
15
|
+
const [parameter, value] = arg.split('=');
|
|
16
|
+
accum[parameter.replace(/-/gi, '')] = value || true;
|
|
17
|
+
return accum;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const nextSwitchOrParameter = process.argv[idx + 1];
|
|
21
|
+
const param = arg.replace(/-/gi, '');
|
|
22
|
+
|
|
23
|
+
if (
|
|
24
|
+
typeof nextSwitchOrParameter === 'undefined' ||
|
|
25
|
+
nextSwitchOrParameter?.startsWith('--')
|
|
26
|
+
) {
|
|
27
|
+
accum[param] = true;
|
|
28
|
+
return accum;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
accum[param] = nextSwitchOrParameter;
|
|
32
|
+
|
|
33
|
+
return accum;
|
|
34
|
+
},
|
|
35
|
+
{} as { [key: string]: string | true },
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const getSourceFiles = async (cwd: string) => {
|
|
40
|
+
const buildDir = path.join(cwd, 'build');
|
|
41
|
+
const files = await fs.readdir(buildDir, { recursive: true });
|
|
42
|
+
const [httpRoutes, webSocketRoutes] = files.reduce(
|
|
43
|
+
([httpRoutes, webSocketRoutes], file) => {
|
|
44
|
+
const parsed = path.parse(file);
|
|
45
|
+
if (parsed.name.endsWith('http')) {
|
|
46
|
+
httpRoutes.push(path.join(buildDir, file));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (parsed.name.endsWith('ws')) {
|
|
50
|
+
webSocketRoutes.push(path.join(buildDir, file));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return [httpRoutes, webSocketRoutes];
|
|
54
|
+
},
|
|
55
|
+
[[] as string[], [] as string[]],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
files,
|
|
60
|
+
httpRoutes,
|
|
61
|
+
webSocketRoutes,
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const camelCase = (str: string) =>
|
|
66
|
+
str.replace(/-([a-z])/g, (_, w) => w.toUpperCase());
|
|
67
|
+
|
|
68
|
+
export const prompt = async (question: string) => {
|
|
69
|
+
const promptLog = debug('browserless.io:prompt');
|
|
70
|
+
const rl = createInterface({
|
|
71
|
+
input: process.stdin,
|
|
72
|
+
output: process.stdout,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return new Promise((resolve) => {
|
|
76
|
+
promptLog(question);
|
|
77
|
+
rl.question(' > ', (a) => {
|
|
78
|
+
rl.close();
|
|
79
|
+
resolve(a.trim());
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const installDependencies = async (
|
|
85
|
+
workingDirectory: string,
|
|
86
|
+
): Promise<void> =>
|
|
87
|
+
new Promise((resolve, reject) => {
|
|
88
|
+
spawn('npm', ['i'], {
|
|
89
|
+
cwd: workingDirectory,
|
|
90
|
+
stdio: 'inherit',
|
|
91
|
+
}).once('close', (code) => {
|
|
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
|
+
|
|
101
|
+
export const buildDockerImage = async (
|
|
102
|
+
cmd: string,
|
|
103
|
+
projectDir: string,
|
|
104
|
+
): Promise<void> =>
|
|
105
|
+
new Promise((resolve, reject) => {
|
|
106
|
+
const [docker, ...args] = cmd.split(' ');
|
|
107
|
+
spawn(docker, args, {
|
|
108
|
+
cwd: projectDir,
|
|
109
|
+
stdio: 'inherit',
|
|
110
|
+
}).once('close', (code) => {
|
|
111
|
+
if (code === 0) {
|
|
112
|
+
return resolve();
|
|
113
|
+
}
|
|
114
|
+
return reject(
|
|
115
|
+
`Error when building Docker image, see output for more details`,
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export const buildTypeScript = async (
|
|
121
|
+
buildDir: string,
|
|
122
|
+
projectDir: string,
|
|
123
|
+
): Promise<void> =>
|
|
124
|
+
new Promise((resolve, reject) => {
|
|
125
|
+
spawn('npx', ['tsc', '--outDir', buildDir], {
|
|
126
|
+
cwd: projectDir,
|
|
127
|
+
stdio: 'inherit',
|
|
128
|
+
}).once('close', (code) => {
|
|
129
|
+
if (code === 0) {
|
|
130
|
+
return resolve();
|
|
131
|
+
}
|
|
132
|
+
return reject(
|
|
133
|
+
`Error in building TypeScript, see output for more details`,
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
});
|
package/src/server.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
BadRequest,
|
|
5
5
|
Config,
|
|
6
6
|
HTTPRoute,
|
|
7
|
+
Hooks,
|
|
7
8
|
Metrics,
|
|
8
9
|
NotFound,
|
|
9
10
|
Request,
|
|
@@ -14,7 +15,6 @@ import {
|
|
|
14
15
|
TooManyRequests,
|
|
15
16
|
Unauthorized,
|
|
16
17
|
WebSocketRoute,
|
|
17
|
-
beforeRequest,
|
|
18
18
|
contentTypes,
|
|
19
19
|
convertPathToURL,
|
|
20
20
|
createLogger,
|
|
@@ -48,6 +48,7 @@ export class HTTPServer extends EventEmitter {
|
|
|
48
48
|
protected metrics: Metrics,
|
|
49
49
|
protected token: Token,
|
|
50
50
|
protected router: Router,
|
|
51
|
+
protected hooks: Hooks,
|
|
51
52
|
) {
|
|
52
53
|
super();
|
|
53
54
|
this.host = config.getHost();
|
|
@@ -111,7 +112,7 @@ export class HTTPServer extends EventEmitter {
|
|
|
111
112
|
);
|
|
112
113
|
|
|
113
114
|
const req = request as Request;
|
|
114
|
-
const proceed = await
|
|
115
|
+
const proceed = await this.hooks.before({ req, res });
|
|
115
116
|
req.parsed = convertPathToURL(request.url || '', this.config);
|
|
116
117
|
shimLegacyRequests(req.parsed);
|
|
117
118
|
|
|
@@ -297,7 +298,7 @@ export class HTTPServer extends EventEmitter {
|
|
|
297
298
|
this.verbose(`Handling inbound WebSocket request on "${request.url}"`);
|
|
298
299
|
|
|
299
300
|
const req = request as Request;
|
|
300
|
-
const proceed = await
|
|
301
|
+
const proceed = await this.hooks.before({ head, req, socket });
|
|
301
302
|
req.parsed = convertPathToURL(request.url || '', this.config);
|
|
302
303
|
shimLegacyRequests(req.parsed);
|
|
303
304
|
|
|
@@ -176,6 +176,10 @@ export default class ChromiumContentPostRoute extends BrowserHTTPRoute {
|
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
const gotoResponse = await gotoCall(content, gotoOptions).catch(
|
|
180
|
+
bestAttemptCatch(bestAttempt),
|
|
181
|
+
);
|
|
182
|
+
|
|
179
183
|
if (addStyleTag.length) {
|
|
180
184
|
for (const tag in addStyleTag) {
|
|
181
185
|
await page.addStyleTag(addStyleTag[tag]);
|
|
@@ -188,10 +192,6 @@ export default class ChromiumContentPostRoute extends BrowserHTTPRoute {
|
|
|
188
192
|
}
|
|
189
193
|
}
|
|
190
194
|
|
|
191
|
-
const gotoResponse = await gotoCall(content, gotoOptions).catch(
|
|
192
|
-
bestAttemptCatch(bestAttempt),
|
|
193
|
-
);
|
|
194
|
-
|
|
195
195
|
if (waitForTimeout) {
|
|
196
196
|
await sleep(waitForTimeout).catch(bestAttemptCatch(bestAttempt));
|
|
197
197
|
}
|
package/src/shared/pdf.http.ts
CHANGED
|
@@ -36,7 +36,6 @@ export interface BodySchema {
|
|
|
36
36
|
addStyleTag?: Array<Parameters<Page['addStyleTag']>[0]>;
|
|
37
37
|
authenticate?: Parameters<Page['authenticate']>[0];
|
|
38
38
|
bestAttempt?: bestAttempt;
|
|
39
|
-
blockModals?: boolean;
|
|
40
39
|
cookies?: Array<Parameters<Page['setCookie']>[0]>;
|
|
41
40
|
emulateMediaType?: Parameters<Page['emulateMediaType']>[0];
|
|
42
41
|
gotoOptions?: Parameters<Page['goto']>[1];
|
|
@@ -185,6 +184,10 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
|
|
|
185
184
|
});
|
|
186
185
|
}
|
|
187
186
|
|
|
187
|
+
const gotoResponse = await gotoCall(content, gotoOptions).catch(
|
|
188
|
+
bestAttemptCatch(bestAttempt),
|
|
189
|
+
);
|
|
190
|
+
|
|
188
191
|
if (addStyleTag.length) {
|
|
189
192
|
for (const tag in addStyleTag) {
|
|
190
193
|
await page.addStyleTag(addStyleTag[tag]);
|
|
@@ -197,10 +200,6 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute {
|
|
|
197
200
|
}
|
|
198
201
|
}
|
|
199
202
|
|
|
200
|
-
const gotoResponse = await gotoCall(content, gotoOptions).catch(
|
|
201
|
-
bestAttemptCatch(bestAttempt),
|
|
202
|
-
);
|
|
203
|
-
|
|
204
203
|
if (waitForTimeout) {
|
|
205
204
|
await sleep(waitForTimeout).catch(bestAttemptCatch(bestAttempt));
|
|
206
205
|
}
|
|
@@ -191,6 +191,10 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
|
|
|
191
191
|
});
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
const gotoResponse = await gotoCall(content, gotoOptions).catch(
|
|
195
|
+
bestAttemptCatch(bestAttempt),
|
|
196
|
+
);
|
|
197
|
+
|
|
194
198
|
if (addStyleTag.length) {
|
|
195
199
|
for (const tag in addStyleTag) {
|
|
196
200
|
await page.addStyleTag(addStyleTag[tag]);
|
|
@@ -203,10 +207,6 @@ export default class ScreenshotPost extends BrowserHTTPRoute {
|
|
|
203
207
|
}
|
|
204
208
|
}
|
|
205
209
|
|
|
206
|
-
const gotoResponse = await gotoCall(content, gotoOptions).catch(
|
|
207
|
-
bestAttemptCatch(bestAttempt),
|
|
208
|
-
);
|
|
209
|
-
|
|
210
210
|
if (waitForTimeout) {
|
|
211
211
|
await sleep(waitForTimeout).catch(bestAttemptCatch(bestAttempt));
|
|
212
212
|
}
|
package/src/utils.ts
CHANGED
|
@@ -745,7 +745,8 @@ export const encrypt = (text: string, secret: Buffer) => {
|
|
|
745
745
|
};
|
|
746
746
|
|
|
747
747
|
export const decrypt = (encryptedText: string, secret: Buffer) => {
|
|
748
|
-
const [encrypted, iv] = encryptedText.split(encryptionSep);
|
|
748
|
+
const [encrypted, iv] = encryptedText.toString().split(encryptionSep);
|
|
749
|
+
console.log('>>', encryptedText.toString());
|
|
749
750
|
if (!iv) throw new ServerError('Bad or invalid encrypted format');
|
|
750
751
|
const decipher = crypto.createDecipheriv(
|
|
751
752
|
encryptionAlgo,
|