@browserless.io/browserless 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/build/browserless.js +4 -2
  3. package/build/browsers/chromium.cdp.d.ts +1 -0
  4. package/build/browsers/chromium.cdp.js +3 -0
  5. package/build/browsers/chromium.playwright.d.ts +1 -0
  6. package/build/browsers/chromium.playwright.js +3 -0
  7. package/build/browsers/firefox.playwright.d.ts +1 -0
  8. package/build/browsers/firefox.playwright.js +3 -0
  9. package/build/browsers/index.d.ts +0 -1
  10. package/build/browsers/index.js +3 -5
  11. package/build/browsers/webkit.playwright.d.ts +1 -0
  12. package/build/browsers/webkit.playwright.js +3 -0
  13. package/build/http.d.ts +1 -0
  14. package/build/http.js +1 -0
  15. package/build/routes/chrome/http/content.post.body.json +8 -8
  16. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  17. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  18. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  19. package/build/routes/chromium/http/content.post.body.json +8 -8
  20. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  21. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  22. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  23. package/build/routes/management/http/pressure.get.d.ts +65 -0
  24. package/build/routes/management/http/pressure.get.js +58 -0
  25. package/build/routes/management/http/pressure.get.response.json +85 -0
  26. package/build/routes/management/tests/management.spec.js +7 -0
  27. package/build/types.d.ts +10 -1
  28. package/build/types.js +10 -1
  29. package/package.json +6 -6
  30. package/src/browserless.ts +4 -0
  31. package/src/browsers/chromium.cdp.ts +4 -0
  32. package/src/browsers/chromium.playwright.ts +4 -0
  33. package/src/browsers/firefox.playwright.ts +4 -0
  34. package/src/browsers/index.ts +3 -5
  35. package/src/browsers/webkit.playwright.ts +4 -0
  36. package/src/http.ts +1 -0
  37. package/src/routes/management/http/pressure.get.ts +139 -0
  38. package/src/routes/management/tests/management.spec.ts +19 -8
  39. package/src/types.ts +9 -0
  40. package/static/docs/swagger.json +147 -10
  41. package/static/docs/swagger.min.json +146 -9
  42. package/static/function/client.js +2749 -2730
  43. package/static/function/index.html +2749 -2730
@@ -0,0 +1,65 @@
1
+ /// <reference types="node" />
2
+ import { APITags, HTTPManagementRoutes, HTTPRoute, Methods, Request, contentTypes } from '@browserless.io/browserless';
3
+ import { ServerResponse } from 'http';
4
+ export type ResponseSchema = {
5
+ pressure: {
6
+ /**
7
+ * An integer representing the percentage of CPU being used. For instance 92 means 92%
8
+ */
9
+ cpu: number | null;
10
+ /**
11
+ * A number of milliseconds since epoch, or "Date.now()" equivalent.
12
+ */
13
+ date: number;
14
+ /**
15
+ * Whether or not a session can be connected and immediately ran on a health instance.
16
+ */
17
+ isAvailable: boolean;
18
+ /**
19
+ * The maximum amount of browsers that can be ran at a single time.
20
+ */
21
+ maxConcurrent: number;
22
+ /**
23
+ * The maximum amount of queued connections allowed at a single time.
24
+ */
25
+ maxQueued: number;
26
+ /**
27
+ * An integer representing the percentage of Memory being used. For instance 95 means 95%
28
+ */
29
+ memory: number | null;
30
+ /**
31
+ * A human-readable message as the overall status of the instance.
32
+ */
33
+ message: string;
34
+ /**
35
+ * The current number of connect or API calls pending to run.
36
+ */
37
+ queued: number;
38
+ /**
39
+ * A simple single-word reason as to why an instance may or may not be available.
40
+ */
41
+ reason: 'full' | 'cpu' | 'memory' | '';
42
+ /**
43
+ * The number of recent connections that were rejected due to the queue and concurrency
44
+ * limits having been filled.
45
+ */
46
+ recentlyRejected: number;
47
+ /**
48
+ * The current number of running connections or API calls.
49
+ */
50
+ running: number;
51
+ };
52
+ };
53
+ export default class PressureGetRoute extends HTTPRoute {
54
+ name: string;
55
+ accepts: contentTypes[];
56
+ auth: boolean;
57
+ browser: null;
58
+ concurrency: boolean;
59
+ contentTypes: contentTypes[];
60
+ description: string;
61
+ method: Methods;
62
+ path: HTTPManagementRoutes;
63
+ tags: APITags[];
64
+ handler: (_req: Request, res: ServerResponse) => Promise<void>;
65
+ }
@@ -0,0 +1,58 @@
1
+ import { APITags, BrowserlessRoutes, HTTPManagementRoutes, HTTPRoute, Methods, contentTypes, jsonResponse, } from '@browserless.io/browserless';
2
+ export default class PressureGetRoute extends HTTPRoute {
3
+ name = BrowserlessRoutes.PressureGetRoute;
4
+ accepts = [contentTypes.any];
5
+ auth = true;
6
+ browser = null;
7
+ concurrency = false;
8
+ contentTypes = [contentTypes.json];
9
+ description = `Returns a JSON body of stats related to the pressure being created on the instance.`;
10
+ method = Methods.get;
11
+ path = HTTPManagementRoutes.pressure;
12
+ tags = [APITags.management];
13
+ handler = async (_req, res) => {
14
+ const monitoring = this.monitoring();
15
+ const config = this.config();
16
+ const limiter = this.limiter();
17
+ const metrics = this.metrics();
18
+ const { cpuInt: cpu, memoryInt: memory, cpuOverloaded, memoryOverloaded, } = await monitoring.overloaded();
19
+ const date = Date.now();
20
+ const hasCapacity = limiter.hasCapacity;
21
+ const queued = limiter.waiting;
22
+ const isAvailable = hasCapacity && !cpuOverloaded && !memoryOverloaded;
23
+ const running = limiter.executing;
24
+ const recentlyRejected = metrics.get().rejected;
25
+ const maxConcurrent = config.getConcurrent();
26
+ const maxQueued = config.getQueued();
27
+ const reason = !hasCapacity
28
+ ? 'full'
29
+ : cpuOverloaded
30
+ ? 'cpu'
31
+ : memoryOverloaded
32
+ ? 'memory'
33
+ : '';
34
+ const message = !hasCapacity
35
+ ? 'Concurrency and queue are full'
36
+ : cpuOverloaded
37
+ ? 'CPU is over the configured maximum for cpu percent'
38
+ : memoryOverloaded
39
+ ? 'Memory is over the configured maximum for memory percent'
40
+ : '';
41
+ const response = {
42
+ pressure: {
43
+ cpu,
44
+ date,
45
+ isAvailable,
46
+ maxConcurrent,
47
+ maxQueued,
48
+ memory,
49
+ message,
50
+ queued,
51
+ reason,
52
+ recentlyRejected,
53
+ running,
54
+ },
55
+ };
56
+ return jsonResponse(res, 200, response);
57
+ };
58
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "pressure": {
5
+ "type": "object",
6
+ "properties": {
7
+ "cpu": {
8
+ "description": "An integer representing the percentage of CPU being used. For instance 92 means 92%",
9
+ "type": [
10
+ "null",
11
+ "number"
12
+ ]
13
+ },
14
+ "date": {
15
+ "description": "A number of milliseconds since epoch, or \"Date.now()\" equivalent.",
16
+ "type": "number"
17
+ },
18
+ "isAvailable": {
19
+ "description": "Whether or not a session can be connected and immediately ran on a health instance.",
20
+ "type": "boolean"
21
+ },
22
+ "maxConcurrent": {
23
+ "description": "The maximum amount of browsers that can be ran at a single time.",
24
+ "type": "number"
25
+ },
26
+ "maxQueued": {
27
+ "description": "The maximum amount of queued connections allowed at a single time.",
28
+ "type": "number"
29
+ },
30
+ "memory": {
31
+ "description": "An integer representing the percentage of Memory being used. For instance 95 means 95%",
32
+ "type": [
33
+ "null",
34
+ "number"
35
+ ]
36
+ },
37
+ "message": {
38
+ "description": "A human-readable message as the overall status of the instance.",
39
+ "type": "string"
40
+ },
41
+ "queued": {
42
+ "description": "The current number of connect or API calls pending to run.",
43
+ "type": "number"
44
+ },
45
+ "reason": {
46
+ "description": "A simple single-word reason as to why an instance may or may not be available.",
47
+ "enum": [
48
+ "",
49
+ "cpu",
50
+ "full",
51
+ "memory"
52
+ ],
53
+ "type": "string"
54
+ },
55
+ "recentlyRejected": {
56
+ "description": "The number of recent connections that were rejected due to the queue and concurrency\nlimits having been filled.",
57
+ "type": "number"
58
+ },
59
+ "running": {
60
+ "description": "The current number of running connections or API calls.",
61
+ "type": "number"
62
+ }
63
+ },
64
+ "additionalProperties": false,
65
+ "required": [
66
+ "cpu",
67
+ "date",
68
+ "isAvailable",
69
+ "maxConcurrent",
70
+ "maxQueued",
71
+ "memory",
72
+ "message",
73
+ "queued",
74
+ "reason",
75
+ "recentlyRejected",
76
+ "running"
77
+ ]
78
+ }
79
+ },
80
+ "additionalProperties": false,
81
+ "required": [
82
+ "pressure"
83
+ ],
84
+ "$schema": "http://json-schema.org/draft-07/schema#"
85
+ }
@@ -31,6 +31,13 @@ describe('Management APIs', function () {
31
31
  expect(res.status).to.equal(200);
32
32
  });
33
33
  });
34
+ it('allows requests to /pressure', async () => {
35
+ await start();
36
+ await fetch('http://localhost:3000/pressure?token=6R0W53R135510').then(async (res) => {
37
+ expect(res.headers.get('content-type')).to.equal('application/json; charset=UTF-8');
38
+ expect(res.status).to.equal(200);
39
+ });
40
+ });
34
41
  it('allows requests to /sessions', async () => {
35
42
  await start();
36
43
  await fetch('http://localhost:3000/sessions?token=6R0W53R135510').then(async (res) => {
package/build/types.d.ts CHANGED
@@ -54,7 +54,8 @@ declare abstract class Route {
54
54
  protected _metrics: Browserless['metrics'];
55
55
  protected _monitoring: Browserless['monitoring'];
56
56
  protected _staticSDKDir: Browserless['staticSDKDir'];
57
- constructor(_browserManager: Browserless['browserManager'], _config: Browserless['config'], _fileSystem: Browserless['fileSystem'], _metrics: Browserless['metrics'], _monitoring: Browserless['monitoring'], _staticSDKDir: Browserless['staticSDKDir']);
57
+ protected _limiter: Browserless['limiter'];
58
+ constructor(_browserManager: Browserless['browserManager'], _config: Browserless['config'], _fileSystem: Browserless['fileSystem'], _metrics: Browserless['metrics'], _monitoring: Browserless['monitoring'], _staticSDKDir: Browserless['staticSDKDir'], _limiter: Browserless['limiter']);
58
59
  /**
59
60
  * A unique name to identify this route. Used in downstream
60
61
  * SDKs to potentially remove or disable.
@@ -133,6 +134,12 @@ declare abstract class Route {
133
134
  * @returns {string | null} The full path location of the SDK's static directory
134
135
  */
135
136
  staticSDKDir: () => string | null;
137
+ /**
138
+ * Helper function that loads the limiter module into the router's
139
+ * handler scope.
140
+ * @returns Limiter
141
+ */
142
+ limiter: () => import("@browserless.io/browserless").Limiter;
136
143
  /**
137
144
  * The HTTP path that this route handles, eg '/my-route' OR an
138
145
  * array of paths that this route can handle.
@@ -510,6 +517,7 @@ export declare const BrowserlessManagementRoutes: {
510
517
  ConfigGetRoute: string;
511
518
  MetricsGetRoute: string;
512
519
  MetricsTotalGetRoute: string;
520
+ PressureGetRoute: string;
513
521
  SessionsGetRoute: string;
514
522
  StaticGetRoute: string;
515
523
  };
@@ -518,6 +526,7 @@ export declare const BrowserlessRoutes: {
518
526
  ConfigGetRoute: string;
519
527
  MetricsGetRoute: string;
520
528
  MetricsTotalGetRoute: string;
529
+ PressureGetRoute: string;
521
530
  SessionsGetRoute: string;
522
531
  StaticGetRoute: string;
523
532
  WebKitPlaywrightWebSocketRoute: string;
package/build/types.js CHANGED
@@ -5,13 +5,15 @@ class Route {
5
5
  _metrics;
6
6
  _monitoring;
7
7
  _staticSDKDir;
8
- constructor(_browserManager, _config, _fileSystem, _metrics, _monitoring, _staticSDKDir) {
8
+ _limiter;
9
+ constructor(_browserManager, _config, _fileSystem, _metrics, _monitoring, _staticSDKDir, _limiter) {
9
10
  this._browserManager = _browserManager;
10
11
  this._config = _config;
11
12
  this._fileSystem = _fileSystem;
12
13
  this._metrics = _metrics;
13
14
  this._monitoring = _monitoring;
14
15
  this._staticSDKDir = _staticSDKDir;
16
+ this._limiter = _limiter;
15
17
  }
16
18
  /**
17
19
  * A boolean, or a function that returns a boolean, on
@@ -86,6 +88,12 @@ class Route {
86
88
  * @returns {string | null} The full path location of the SDK's static directory
87
89
  */
88
90
  staticSDKDir = () => this._staticSDKDir;
91
+ /**
92
+ * Helper function that loads the limiter module into the router's
93
+ * handler scope.
94
+ * @returns Limiter
95
+ */
96
+ limiter = () => this._limiter;
89
97
  }
90
98
  /**
91
99
  * A primitive HTTP-based route that doesn't require a
@@ -184,6 +192,7 @@ export const BrowserlessManagementRoutes = {
184
192
  ConfigGetRoute: 'ConfigGetRoute',
185
193
  MetricsGetRoute: 'MetricsGetRoute',
186
194
  MetricsTotalGetRoute: 'MetricsTotalGetRoute',
195
+ PressureGetRoute: 'PressureGetRoute',
187
196
  SessionsGetRoute: 'SessionsGetRoute',
188
197
  StaticGetRoute: 'StaticGetRoute',
189
198
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserless.io/browserless",
3
- "version": "2.9.0",
3
+ "version": "2.11.0",
4
4
  "license": "SSPL",
5
5
  "description": "The browserless platform",
6
6
  "author": "browserless.io",
@@ -59,11 +59,11 @@
59
59
  "lighthouse": "^12.0.0",
60
60
  "micromatch": "^4.0.4",
61
61
  "playwright-core": "1.44.0",
62
- "puppeteer-core": "^22.8.0",
62
+ "puppeteer-core": "^22.8.1",
63
63
  "puppeteer-extra": "^3.3.6",
64
64
  "puppeteer-extra-plugin-stealth": "^2.11.2",
65
65
  "queue": "^7.0.0",
66
- "systeminformation": "^5.22.8",
66
+ "systeminformation": "^5.22.9",
67
67
  "tar-fs": "^3.0.6"
68
68
  },
69
69
  "optionalDependencies": {
@@ -73,10 +73,10 @@
73
73
  "@types/http-proxy": "^1.17.14",
74
74
  "@types/micromatch": "^4.0.7",
75
75
  "@types/mocha": "^10.0.6",
76
- "@types/node": "^20.12.11",
76
+ "@types/node": "^20.12.12",
77
77
  "@types/sinon": "^17.0.3",
78
- "@typescript-eslint/eslint-plugin": "^7.8.0",
79
- "@typescript-eslint/parser": "^7.8.0",
78
+ "@typescript-eslint/eslint-plugin": "^7.9.0",
79
+ "@typescript-eslint/parser": "^7.9.0",
80
80
  "assert": "^2.0.0",
81
81
  "chai": "^5.1.1",
82
82
  "cross-env": "^7.0.3",
@@ -272,12 +272,14 @@ export class Browserless extends EventEmitter {
272
272
  this.metrics,
273
273
  this.monitoring,
274
274
  this.staticSDKDir,
275
+ this.limiter,
275
276
  );
276
277
 
277
278
  if (!this.routeIsDisabled(route)) {
278
279
  route.bodySchema = safeParse(bodySchema);
279
280
  route.querySchema = safeParse(querySchema);
280
281
  route.config = () => this.config;
282
+ route.limiter = () => this.limiter;
281
283
  route.metrics = () => this.metrics;
282
284
  route.monitoring = () => this.monitoring;
283
285
  route.fileSystem = () => this.fileSystem;
@@ -321,11 +323,13 @@ export class Browserless extends EventEmitter {
321
323
  this.metrics,
322
324
  this.monitoring,
323
325
  this.staticSDKDir,
326
+ this.limiter,
324
327
  );
325
328
 
326
329
  if (!this.routeIsDisabled(route)) {
327
330
  route.querySchema = safeParse(querySchema);
328
331
  route.config = () => this.config;
332
+ route.limiter = () => this.limiter;
329
333
  route.metrics = () => this.metrics;
330
334
  route.monitoring = () => this.monitoring;
331
335
  route.fileSystem = () => this.fileSystem;
@@ -59,6 +59,10 @@ export class ChromiumCDP extends EventEmitter {
59
59
  this.removeAllListeners();
60
60
  }
61
61
 
62
+ public keepAlive() {
63
+ return false;
64
+ }
65
+
62
66
  public getPageId = (page: Page): string => {
63
67
  // @ts-ignore
64
68
  return page.target()._targetId;
@@ -43,6 +43,10 @@ export class ChromiumPlaywright extends EventEmitter {
43
43
  this.removeAllListeners();
44
44
  }
45
45
 
46
+ public keepAlive() {
47
+ return false;
48
+ }
49
+
46
50
  public isRunning = (): boolean => this.running;
47
51
 
48
52
  public close = async (): Promise<void> => {
@@ -42,6 +42,10 @@ export class FirefoxPlaywright extends EventEmitter {
42
42
  this.removeAllListeners();
43
43
  }
44
44
 
45
+ public keepAlive() {
46
+ return false;
47
+ }
48
+
45
49
  public isRunning = (): boolean => this.running;
46
50
 
47
51
  public close = async (): Promise<void> => {
@@ -36,7 +36,6 @@ import path from 'path';
36
36
 
37
37
  export class BrowserManager {
38
38
  protected browsers: Map<BrowserInstance, BrowserlessSession> = new Map();
39
- protected launching: Map<string, Promise<unknown>> = new Map();
40
39
  protected timers: Map<string, number> = new Map();
41
40
  protected log = new Logger('browser-manager');
42
41
  protected chromeBrowsers = [ChromiumCDP, ChromeCDP];
@@ -272,7 +271,7 @@ export class BrowserManager {
272
271
  this.log.info(`${session.numbConnected} Client(s) are currently connected`);
273
272
 
274
273
  // Don't close if there's clients still connected
275
- if (session.numbConnected > 0) {
274
+ if (session.numbConnected > 0 || browser.keepAlive()) {
276
275
  return;
277
276
  }
278
277
 
@@ -314,7 +313,6 @@ export class BrowserManager {
314
313
 
315
314
  if (id && resolver) {
316
315
  resolver(null);
317
- this.launching.delete(id);
318
316
  }
319
317
 
320
318
  --session.numbConnected;
@@ -457,7 +455,7 @@ export class BrowserManager {
457
455
  userDataDir,
458
456
  });
459
457
 
460
- const connectionMeta: BrowserlessSession = {
458
+ const session: BrowserlessSession = {
461
459
  id: null,
462
460
  initialConnectURL:
463
461
  path.join(req.parsed.pathname, req.parsed.search) || '',
@@ -471,7 +469,7 @@ export class BrowserManager {
471
469
  userDataDir,
472
470
  };
473
471
 
474
- this.browsers.set(browser, connectionMeta);
472
+ this.browsers.set(browser, session);
475
473
 
476
474
  await browser.launch(launchOptions as object);
477
475
  await this.hooks.browser({ browser, meta: req.parsed });
@@ -42,6 +42,10 @@ export class WebkitPlaywright extends EventEmitter {
42
42
  this.removeAllListeners();
43
43
  }
44
44
 
45
+ public keepAlive() {
46
+ return false;
47
+ }
48
+
45
49
  public isRunning = (): boolean => this.running;
46
50
 
47
51
  public close = async (): Promise<void> => {
package/src/http.ts CHANGED
@@ -128,6 +128,7 @@ export enum HTTPManagementRoutes {
128
128
  config = '/config',
129
129
  metrics = '/metrics',
130
130
  metricsTotal = '/metrics/total',
131
+ pressure = '/pressure',
131
132
  sessions = '/sessions',
132
133
  static = '/',
133
134
  }
@@ -0,0 +1,139 @@
1
+ import {
2
+ APITags,
3
+ BrowserlessRoutes,
4
+ HTTPManagementRoutes,
5
+ HTTPRoute,
6
+ Methods,
7
+ Request,
8
+ contentTypes,
9
+ jsonResponse,
10
+ } from '@browserless.io/browserless';
11
+ import { ServerResponse } from 'http';
12
+
13
+ export type ResponseSchema = {
14
+ pressure: {
15
+ /**
16
+ * An integer representing the percentage of CPU being used. For instance 92 means 92%
17
+ */
18
+ cpu: number | null;
19
+
20
+ /**
21
+ * A number of milliseconds since epoch, or "Date.now()" equivalent.
22
+ */
23
+ date: number;
24
+
25
+ /**
26
+ * Whether or not a session can be connected and immediately ran on a health instance.
27
+ */
28
+ isAvailable: boolean;
29
+
30
+ /**
31
+ * The maximum amount of browsers that can be ran at a single time.
32
+ */
33
+ maxConcurrent: number;
34
+
35
+ /**
36
+ * The maximum amount of queued connections allowed at a single time.
37
+ */
38
+ maxQueued: number;
39
+
40
+ /**
41
+ * An integer representing the percentage of Memory being used. For instance 95 means 95%
42
+ */
43
+ memory: number | null;
44
+
45
+ /**
46
+ * A human-readable message as the overall status of the instance.
47
+ */
48
+ message: string;
49
+
50
+ /**
51
+ * The current number of connect or API calls pending to run.
52
+ */
53
+ queued: number;
54
+
55
+ /**
56
+ * A simple single-word reason as to why an instance may or may not be available.
57
+ */
58
+ reason: 'full' | 'cpu' | 'memory' | '';
59
+
60
+ /**
61
+ * The number of recent connections that were rejected due to the queue and concurrency
62
+ * limits having been filled.
63
+ */
64
+ recentlyRejected: number;
65
+
66
+ /**
67
+ * The current number of running connections or API calls.
68
+ */
69
+ running: number;
70
+ };
71
+ };
72
+
73
+ export default class PressureGetRoute extends HTTPRoute {
74
+ name = BrowserlessRoutes.PressureGetRoute;
75
+ accepts = [contentTypes.any];
76
+ auth = true;
77
+ browser = null;
78
+ concurrency = false;
79
+ contentTypes = [contentTypes.json];
80
+ description = `Returns a JSON body of stats related to the pressure being created on the instance.`;
81
+ method = Methods.get;
82
+ path = HTTPManagementRoutes.pressure;
83
+ tags = [APITags.management];
84
+ handler = async (_req: Request, res: ServerResponse): Promise<void> => {
85
+ const monitoring = this.monitoring();
86
+ const config = this.config();
87
+ const limiter = this.limiter();
88
+ const metrics = this.metrics();
89
+
90
+ const {
91
+ cpuInt: cpu,
92
+ memoryInt: memory,
93
+ cpuOverloaded,
94
+ memoryOverloaded,
95
+ } = await monitoring.overloaded();
96
+ const date = Date.now();
97
+ const hasCapacity = limiter.hasCapacity;
98
+ const queued = limiter.waiting;
99
+ const isAvailable = hasCapacity && !cpuOverloaded && !memoryOverloaded;
100
+ const running = limiter.executing;
101
+ const recentlyRejected = metrics.get().rejected;
102
+ const maxConcurrent = config.getConcurrent();
103
+ const maxQueued = config.getQueued();
104
+
105
+ const reason = !hasCapacity
106
+ ? 'full'
107
+ : cpuOverloaded
108
+ ? 'cpu'
109
+ : memoryOverloaded
110
+ ? 'memory'
111
+ : '';
112
+
113
+ const message = !hasCapacity
114
+ ? 'Concurrency and queue are full'
115
+ : cpuOverloaded
116
+ ? 'CPU is over the configured maximum for cpu percent'
117
+ : memoryOverloaded
118
+ ? 'Memory is over the configured maximum for memory percent'
119
+ : '';
120
+
121
+ const response: ResponseSchema = {
122
+ pressure: {
123
+ cpu,
124
+ date,
125
+ isAvailable,
126
+ maxConcurrent,
127
+ maxQueued,
128
+ memory,
129
+ message,
130
+ queued,
131
+ reason,
132
+ recentlyRejected,
133
+ running,
134
+ },
135
+ };
136
+
137
+ return jsonResponse(res, 200, response);
138
+ };
139
+ }
@@ -56,6 +56,19 @@ describe('Management APIs', function () {
56
56
  );
57
57
  });
58
58
 
59
+ it('allows requests to /pressure', async () => {
60
+ await start();
61
+
62
+ await fetch('http://localhost:3000/pressure?token=6R0W53R135510').then(
63
+ async (res) => {
64
+ expect(res.headers.get('content-type')).to.equal(
65
+ 'application/json; charset=UTF-8',
66
+ );
67
+ expect(res.status).to.equal(200);
68
+ },
69
+ );
70
+ });
71
+
59
72
  it('allows requests to /sessions', async () => {
60
73
  await start();
61
74
 
@@ -87,13 +100,11 @@ describe('Management APIs', function () {
87
100
 
88
101
  await fetch('http://localhost:3000/active?token=6R0W53R135510', {
89
102
  method: 'HEAD',
90
- }).then(
91
- async (res) => {
92
- expect(res.headers.get('content-type')).to.equal(
93
- 'text/plain; charset=UTF-8',
94
- );
95
- expect(res.status).to.equal(204);
96
- },
97
- );
103
+ }).then(async (res) => {
104
+ expect(res.headers.get('content-type')).to.equal(
105
+ 'text/plain; charset=UTF-8',
106
+ );
107
+ expect(res.status).to.equal(204);
108
+ });
98
109
  });
99
110
  });
package/src/types.ts CHANGED
@@ -103,6 +103,7 @@ abstract class Route {
103
103
  protected _metrics: Browserless['metrics'],
104
104
  protected _monitoring: Browserless['monitoring'],
105
105
  protected _staticSDKDir: Browserless['staticSDKDir'],
106
+ protected _limiter: Browserless['limiter'],
106
107
  ) {}
107
108
 
108
109
  /**
@@ -196,6 +197,13 @@ abstract class Route {
196
197
  */
197
198
  staticSDKDir = () => this._staticSDKDir;
198
199
 
200
+ /**
201
+ * Helper function that loads the limiter module into the router's
202
+ * handler scope.
203
+ * @returns Limiter
204
+ */
205
+ limiter = () => this._limiter;
206
+
199
207
  /**
200
208
  * The HTTP path that this route handles, eg '/my-route' OR an
201
209
  * array of paths that this route can handle.
@@ -650,6 +658,7 @@ export const BrowserlessManagementRoutes = {
650
658
  ConfigGetRoute: 'ConfigGetRoute',
651
659
  MetricsGetRoute: 'MetricsGetRoute',
652
660
  MetricsTotalGetRoute: 'MetricsTotalGetRoute',
661
+ PressureGetRoute: 'PressureGetRoute',
653
662
  SessionsGetRoute: 'SessionsGetRoute',
654
663
  StaticGetRoute: 'StaticGetRoute',
655
664
  };