@browserless.io/browserless 2.12.0-beta-3 → 2.12.0-beta-6

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 (199) hide show
  1. package/build/browserless.d.ts +3 -3
  2. package/build/browserless.js +6 -6
  3. package/build/browsers/chromium.cdp.d.ts +13 -13
  4. package/build/browsers/chromium.cdp.js +79 -67
  5. package/build/browsers/chromium.playwright.d.ts +12 -12
  6. package/build/browsers/chromium.playwright.js +44 -36
  7. package/build/browsers/firefox.playwright.d.ts +12 -12
  8. package/build/browsers/firefox.playwright.js +44 -36
  9. package/build/browsers/index.d.ts +17 -16
  10. package/build/browsers/index.js +63 -42
  11. package/build/browsers/webkit.playwright.d.ts +12 -12
  12. package/build/browsers/webkit.playwright.js +47 -39
  13. package/build/config.d.ts +65 -63
  14. package/build/config.js +162 -102
  15. package/build/file-system.d.ts +4 -4
  16. package/build/file-system.js +8 -8
  17. package/build/hooks.d.ts +2 -2
  18. package/build/hooks.js +4 -4
  19. package/build/limiter.d.ts +4 -4
  20. package/build/limiter.js +10 -10
  21. package/build/logger.d.ts +6 -6
  22. package/build/logger.js +12 -12
  23. package/build/metrics.d.ts +12 -12
  24. package/build/metrics.js +23 -23
  25. package/build/monitoring.d.ts +4 -4
  26. package/build/monitoring.js +8 -8
  27. package/build/router.d.ts +8 -8
  28. package/build/router.js +74 -70
  29. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  30. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  31. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  32. package/build/routes/chromium/http/content.post.body.json +8 -8
  33. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  34. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  35. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  36. package/build/routes/firefox/ws/playwright.d.ts +1 -1
  37. package/build/routes/firefox/ws/playwright.js +2 -2
  38. package/build/routes/management/http/active.get.d.ts +1 -1
  39. package/build/routes/management/http/active.get.js +3 -1
  40. package/build/routes/management/http/config.get.d.ts +1 -1
  41. package/build/routes/management/http/config.get.js +2 -2
  42. package/build/routes/management/http/metrics-total.get.d.ts +1 -1
  43. package/build/routes/management/http/metrics-total.get.js +2 -2
  44. package/build/routes/management/http/metrics.get.d.ts +1 -1
  45. package/build/routes/management/http/metrics.get.js +2 -2
  46. package/build/routes/management/http/pressure.get.d.ts +1 -1
  47. package/build/routes/management/http/pressure.get.js +2 -2
  48. package/build/routes/management/http/sessions.get.d.ts +1 -1
  49. package/build/routes/management/http/sessions.get.js +2 -2
  50. package/build/routes/management/http/static.get.d.ts +1 -1
  51. package/build/routes/management/http/static.get.js +2 -2
  52. package/build/routes/webkit/ws/playwright.d.ts +1 -1
  53. package/build/routes/webkit/ws/playwright.js +2 -2
  54. package/build/server.d.ts +5 -5
  55. package/build/server.js +11 -11
  56. package/build/shared/browser.ws.d.ts +1 -1
  57. package/build/shared/browser.ws.js +3 -1
  58. package/build/shared/chromium.playwright.ws.d.ts +1 -1
  59. package/build/shared/chromium.playwright.ws.js +2 -2
  60. package/build/shared/chromium.ws.d.ts +1 -1
  61. package/build/shared/chromium.ws.js +3 -1
  62. package/build/shared/content.http.d.ts +1 -1
  63. package/build/shared/content.http.js +2 -2
  64. package/build/shared/download.http.d.ts +1 -1
  65. package/build/shared/download.http.js +60 -58
  66. package/build/shared/function.http.d.ts +1 -1
  67. package/build/shared/function.http.js +2 -2
  68. package/build/shared/json-list.http.d.ts +1 -1
  69. package/build/shared/json-list.http.js +2 -2
  70. package/build/shared/json-new.http.d.ts +1 -1
  71. package/build/shared/json-new.http.js +2 -2
  72. package/build/shared/json-protocol.http.d.ts +1 -1
  73. package/build/shared/json-protocol.http.js +2 -2
  74. package/build/shared/json-version.http.d.ts +1 -1
  75. package/build/shared/json-version.http.js +2 -2
  76. package/build/shared/page.ws.d.ts +1 -1
  77. package/build/shared/page.ws.js +3 -1
  78. package/build/shared/pdf.http.d.ts +1 -1
  79. package/build/shared/pdf.http.js +2 -2
  80. package/build/shared/performance.http.d.ts +1 -1
  81. package/build/shared/performance.http.js +2 -2
  82. package/build/shared/scrape.http.d.ts +1 -1
  83. package/build/shared/scrape.http.js +2 -2
  84. package/build/shared/screenshot.http.d.ts +1 -1
  85. package/build/shared/screenshot.http.js +2 -2
  86. package/build/shared/utils/function/client.d.ts +1 -1
  87. package/build/shared/utils/function/client.js +3 -1
  88. package/build/shim.d.ts +1 -1
  89. package/build/shim.js +2 -2
  90. package/build/token.d.ts +3 -3
  91. package/build/token.js +6 -6
  92. package/build/types.d.ts +5 -5
  93. package/build/webhooks.d.ts +2 -2
  94. package/build/webhooks.js +4 -4
  95. package/extensions/ublock/_locales/eu/messages.json +4 -4
  96. package/extensions/ublock/_locales/hi/messages.json +5 -5
  97. package/extensions/ublock/_locales/kn/messages.json +11 -11
  98. package/extensions/ublock/_locales/nb/messages.json +2 -2
  99. package/extensions/ublock/_locales/no/messages.json +2 -2
  100. package/extensions/ublock/_locales/ro/messages.json +1 -1
  101. package/extensions/ublock/_locales/sv/messages.json +1 -1
  102. package/extensions/ublock/_locales/zh_CN/messages.json +2 -2
  103. package/extensions/ublock/assets/assets.json +3 -8
  104. package/extensions/ublock/assets/resources/scriptlets.js +128 -31
  105. package/extensions/ublock/assets/thirdparties/easylist/easylist.txt +4870 -3560
  106. package/extensions/ublock/assets/thirdparties/easylist/easyprivacy.txt +662 -173
  107. package/extensions/ublock/assets/thirdparties/pgl.yoyo.org/as/serverlist +10 -42
  108. package/extensions/ublock/assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat +241 -80
  109. package/extensions/ublock/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt +2093 -1224
  110. package/extensions/ublock/assets/ublock/badlists.txt +2 -0
  111. package/extensions/ublock/assets/ublock/badware.min.txt +408 -287
  112. package/extensions/ublock/assets/ublock/filters.min.txt +947 -645
  113. package/extensions/ublock/assets/ublock/privacy.min.txt +43 -8
  114. package/extensions/ublock/assets/ublock/quick-fixes.min.txt +55 -93
  115. package/extensions/ublock/assets/ublock/unbreak.min.txt +52 -19
  116. package/extensions/ublock/css/1p-filters.css +2 -0
  117. package/extensions/ublock/css/codemirror.css +2 -2
  118. package/extensions/ublock/css/dashboard.css +2 -5
  119. package/extensions/ublock/css/epicker-ui.css +3 -3
  120. package/extensions/ublock/css/fa-icons.css +3 -0
  121. package/extensions/ublock/css/logger-ui-inspector.css +1 -0
  122. package/extensions/ublock/css/logger-ui.css +44 -32
  123. package/extensions/ublock/img/fontawesome/fontawesome-defs.svg +1 -0
  124. package/extensions/ublock/js/3p-filters.js +4 -5
  125. package/extensions/ublock/js/biditrie.js +16 -11
  126. package/extensions/ublock/js/cachestorage.js +37 -37
  127. package/extensions/ublock/js/contentscript-extra.js +0 -2
  128. package/extensions/ublock/js/contentscript.js +1 -6
  129. package/extensions/ublock/js/epicker-ui.js +28 -36
  130. package/extensions/ublock/js/fa-icons.js +1 -0
  131. package/extensions/ublock/js/hntrie.js +19 -13
  132. package/extensions/ublock/js/logger-ui-inspector.js +6 -13
  133. package/extensions/ublock/js/logger-ui.js +264 -264
  134. package/extensions/ublock/js/s14e-serializer.js +267 -264
  135. package/extensions/ublock/js/scriptlet-filtering.js +12 -18
  136. package/extensions/ublock/js/scriptlets/dom-inspector.js +1 -5
  137. package/extensions/ublock/js/scriptlets/epicker.js +53 -59
  138. package/extensions/ublock/js/start.js +0 -8
  139. package/extensions/ublock/js/storage.js +2 -9
  140. package/extensions/ublock/js/vapi-background.js +19 -20
  141. package/extensions/ublock/js/vapi-common.js +2 -7
  142. package/extensions/ublock/js/vapi.js +0 -4
  143. package/extensions/ublock/js/webext.js +23 -15
  144. package/extensions/ublock/logger-ui.html +24 -15
  145. package/extensions/ublock/manifest.json +2 -3
  146. package/package.json +4 -4
  147. package/src/browserless.ts +6 -6
  148. package/src/browsers/chromium.cdp.ts +35 -25
  149. package/src/browsers/chromium.playwright.ts +30 -23
  150. package/src/browsers/firefox.playwright.ts +30 -24
  151. package/src/browsers/index.ts +81 -50
  152. package/src/browsers/webkit.playwright.ts +33 -26
  153. package/src/config.ts +166 -104
  154. package/src/file-system.ts +9 -9
  155. package/src/hooks.ts +4 -4
  156. package/src/limiter.ts +12 -12
  157. package/src/logger.ts +12 -12
  158. package/src/metrics.ts +23 -23
  159. package/src/monitoring.ts +9 -9
  160. package/src/router.ts +31 -31
  161. package/src/routes/chrome/tests/websocket.spec.ts +2 -2
  162. package/src/routes/chromium/tests/websocket.spec.ts +2 -2
  163. package/src/routes/firefox/tests/websocket.spec.ts +2 -4
  164. package/src/routes/firefox/ws/playwright.ts +3 -3
  165. package/src/routes/management/http/active.get.ts +3 -2
  166. package/src/routes/management/http/config.get.ts +2 -2
  167. package/src/routes/management/http/metrics-total.get.ts +2 -2
  168. package/src/routes/management/http/metrics.get.ts +2 -2
  169. package/src/routes/management/http/pressure.get.ts +2 -2
  170. package/src/routes/management/http/sessions.get.ts +2 -2
  171. package/src/routes/management/http/static.get.ts +3 -3
  172. package/src/routes/webkit/tests/websocket.spec.ts +2 -3
  173. package/src/routes/webkit/ws/playwright.ts +3 -3
  174. package/src/server.ts +14 -16
  175. package/src/shared/browser.ws.ts +4 -2
  176. package/src/shared/chromium.playwright.ws.ts +3 -3
  177. package/src/shared/chromium.ws.ts +4 -2
  178. package/src/shared/content.http.ts +3 -3
  179. package/src/shared/download.http.ts +4 -3
  180. package/src/shared/function.http.ts +3 -3
  181. package/src/shared/json-list.http.ts +2 -2
  182. package/src/shared/json-new.http.ts +2 -2
  183. package/src/shared/json-protocol.http.ts +2 -6
  184. package/src/shared/json-version.http.ts +2 -6
  185. package/src/shared/page.ws.ts +4 -2
  186. package/src/shared/pdf.http.ts +3 -3
  187. package/src/shared/performance.http.ts +3 -3
  188. package/src/shared/scrape.http.ts +3 -3
  189. package/src/shared/screenshot.http.ts +3 -3
  190. package/src/shared/utils/function/client.ts +3 -1
  191. package/src/shim.ts +2 -2
  192. package/src/token.ts +7 -7
  193. package/src/types.ts +8 -8
  194. package/src/utils.ts +1 -1
  195. package/src/webhooks.ts +4 -4
  196. package/static/docs/swagger.json +1 -1
  197. package/static/docs/swagger.min.json +1 -1
  198. package/static/function/client.js +3 -1
  199. package/static/function/index.html +3 -1
@@ -59,16 +59,16 @@ export class ChromiumCDP extends EventEmitter {
59
59
  this.removeAllListeners();
60
60
  }
61
61
 
62
- public keepAlive() {
63
- return false;
62
+ public keepUntil() {
63
+ return 0;
64
64
  }
65
65
 
66
- public getPageId = (page: Page): string => {
66
+ public getPageId(page: Page): string {
67
67
  // @ts-ignore
68
68
  return page.target()._targetId;
69
- };
69
+ }
70
70
 
71
- protected onTargetCreated = async (target: Target) => {
71
+ protected async onTargetCreated(target: Target) {
72
72
  if (target.type() === 'page') {
73
73
  const page = await target.page().catch((e) => {
74
74
  this.logger.error(`Error in ${this.constructor.name} new page ${e}`);
@@ -130,11 +130,13 @@ export class ChromiumCDP extends EventEmitter {
130
130
  this.emit('newPage', page);
131
131
  }
132
132
  }
133
- };
133
+ }
134
134
 
135
- public isRunning = (): boolean => this.running;
135
+ public isRunning(): boolean {
136
+ return this.running;
137
+ }
136
138
 
137
- public newPage = async (): Promise<Page> => {
139
+ public async newPage(): Promise<Page> {
138
140
  if (!this.browser) {
139
141
  throw new ServerError(
140
142
  `${this.constructor.name} hasn't been launched yet!`,
@@ -142,9 +144,9 @@ export class ChromiumCDP extends EventEmitter {
142
144
  }
143
145
 
144
146
  return this.browser.newPage();
145
- };
147
+ }
146
148
 
147
- public close = async (): Promise<void> => {
149
+ public async close(): Promise<void> {
148
150
  if (this.browser) {
149
151
  this.logger.info(
150
152
  `Closing ${this.constructor.name} process and all listeners`,
@@ -157,13 +159,17 @@ export class ChromiumCDP extends EventEmitter {
157
159
  this.browser = null;
158
160
  this.browserWSEndpoint = null;
159
161
  }
160
- };
162
+ }
161
163
 
162
- public pages = async (): Promise<Page[]> => this.browser?.pages() || [];
164
+ public async pages(): Promise<Page[]> {
165
+ return this.browser?.pages() || [];
166
+ }
163
167
 
164
- public process = () => this.browser?.process() || null;
168
+ public process() {
169
+ return this.browser?.process() || null;
170
+ }
165
171
 
166
- public launch = async (options: CDPLaunchOptions = {}): Promise<Browser> => {
172
+ public async launch(options: CDPLaunchOptions = {}): Promise<Browser> {
167
173
  this.port = await getPort();
168
174
  this.logger.info(`${this.constructor.name} got open port ${this.port}`);
169
175
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -205,7 +211,7 @@ export class ChromiumCDP extends EventEmitter {
205
211
  `Launching ${this.constructor.name} Handler`,
206
212
  );
207
213
  this.browser = (await launch(finalOptions)) as Browser;
208
- this.browser.on('targetcreated', this.onTargetCreated);
214
+ this.browser.on('targetcreated', this.onTargetCreated.bind(this));
209
215
  this.running = true;
210
216
  this.browserWSEndpoint = this.browser.wsEndpoint();
211
217
  this.logger.info(
@@ -213,11 +219,13 @@ export class ChromiumCDP extends EventEmitter {
213
219
  );
214
220
 
215
221
  return this.browser;
216
- };
222
+ }
217
223
 
218
- public wsEndpoint = (): string | null => this.browserWSEndpoint;
224
+ public wsEndpoint(): string | null {
225
+ return this.browserWSEndpoint;
226
+ }
219
227
 
220
- public publicWSEndpoint = (token: string | null): string | null => {
228
+ public publicWSEndpoint(token: string | null): string | null {
221
229
  if (!this.browserWSEndpoint) {
222
230
  return null;
223
231
  }
@@ -232,14 +240,14 @@ export class ChromiumCDP extends EventEmitter {
232
240
  }
233
241
 
234
242
  return externalURL.href;
235
- };
243
+ }
236
244
 
237
- public proxyPageWebSocket = async (
245
+ public async proxyPageWebSocket(
238
246
  req: Request,
239
247
  socket: Duplex,
240
248
  head: Buffer,
241
- ): Promise<void> =>
242
- new Promise(async (resolve, reject) => {
249
+ ): Promise<void> {
250
+ return new Promise(async (resolve, reject) => {
243
251
  if (!this.browserWSEndpoint || !this.browser) {
244
252
  throw new ServerError(
245
253
  `No browserWSEndpoint found, did you launch first?`,
@@ -280,13 +288,14 @@ export class ChromiumCDP extends EventEmitter {
280
288
  },
281
289
  );
282
290
  });
291
+ }
283
292
 
284
- public proxyWebSocket = async (
293
+ public async proxyWebSocket(
285
294
  req: Request,
286
295
  socket: Duplex,
287
296
  head: Buffer,
288
- ): Promise<void> =>
289
- new Promise((resolve, reject) => {
297
+ ): Promise<void> {
298
+ return new Promise((resolve, reject) => {
290
299
  if (!this.browserWSEndpoint) {
291
300
  throw new ServerError(
292
301
  `No browserWSEndpoint found, did you launch first?`,
@@ -330,4 +339,5 @@ export class ChromiumCDP extends EventEmitter {
330
339
  },
331
340
  );
332
341
  });
342
+ }
333
343
  }
@@ -43,13 +43,15 @@ export class ChromiumPlaywright extends EventEmitter {
43
43
  this.removeAllListeners();
44
44
  }
45
45
 
46
- public keepAlive() {
47
- return false;
46
+ public keepUntil() {
47
+ return 0;
48
48
  }
49
49
 
50
- public isRunning = (): boolean => this.running;
50
+ public isRunning(): boolean {
51
+ return this.running;
52
+ }
51
53
 
52
- public close = async (): Promise<void> => {
54
+ public async close(): Promise<void> {
53
55
  if (this.browser) {
54
56
  this.logger.info(
55
57
  `Closing ${this.constructor.name} process and all listeners`,
@@ -61,23 +63,25 @@ export class ChromiumPlaywright extends EventEmitter {
61
63
  this.browser = null;
62
64
  this.browserWSEndpoint = null;
63
65
  }
64
- };
66
+ }
65
67
 
66
- public pages = async (): Promise<[]> => [];
68
+ public async pages(): Promise<[]> {
69
+ return [];
70
+ }
67
71
 
68
- public getPageId = (): string => {
72
+ public getPageId(): string {
69
73
  throw new ServerError(
70
74
  `#getPageId is not yet supported with ${this.constructor.name}.`,
71
75
  );
72
- };
76
+ }
73
77
 
74
- public makeLiveURL = (): void => {
78
+ public makeLiveURL(): void {
75
79
  throw new ServerError(
76
80
  `Live URLs are not yet supported with ${this.constructor.name}. In the future this will be at "${this.config.getExternalAddress()}"`,
77
81
  );
78
- };
82
+ }
79
83
 
80
- public newPage = async (): Promise<Page> => {
84
+ public async newPage(): Promise<Page> {
81
85
  if (!this.browser || !this.browserWSEndpoint) {
82
86
  throw new ServerError(
83
87
  `${this.constructor.name} hasn't been launched yet!`,
@@ -85,12 +89,12 @@ export class ChromiumPlaywright extends EventEmitter {
85
89
  }
86
90
  const browser = await playwright.chromium.connect(this.browserWSEndpoint);
87
91
  return await browser.newPage();
88
- };
92
+ }
89
93
 
90
- public launch = async (
94
+ public async launch(
91
95
  options: BrowserServerOptions = {},
92
96
  version?: string,
93
- ): Promise<playwright.BrowserServer> => {
97
+ ): Promise<playwright.BrowserServer> {
94
98
  this.logger.info(`Launching ${this.constructor.name} Handler`);
95
99
 
96
100
  const opts = {
@@ -115,11 +119,13 @@ export class ChromiumPlaywright extends EventEmitter {
115
119
  this.browserWSEndpoint = browserWSEndpoint;
116
120
 
117
121
  return this.browser;
118
- };
122
+ }
119
123
 
120
- public wsEndpoint = (): string | null => this.browserWSEndpoint;
124
+ public wsEndpoint(): string | null {
125
+ return this.browserWSEndpoint;
126
+ }
121
127
 
122
- public publicWSEndpoint = (token: string | null): string | null => {
128
+ public publicWSEndpoint(token: string | null): string | null {
123
129
  if (!this.browserWSEndpoint) {
124
130
  return null;
125
131
  }
@@ -134,18 +140,18 @@ export class ChromiumPlaywright extends EventEmitter {
134
140
  }
135
141
 
136
142
  return externalURL.href;
137
- };
143
+ }
138
144
 
139
- public proxyPageWebSocket = async () => {
145
+ public async proxyPageWebSocket() {
140
146
  this.logger.warn(`${this.constructor.name} Not yet implemented`);
141
- };
147
+ }
142
148
 
143
- public proxyWebSocket = async (
149
+ public async proxyWebSocket(
144
150
  req: Request,
145
151
  socket: Duplex,
146
152
  head: Buffer,
147
- ): Promise<void> =>
148
- new Promise((resolve, reject) => {
153
+ ): Promise<void> {
154
+ return new Promise((resolve, reject) => {
149
155
  if (!this.browserWSEndpoint) {
150
156
  throw new ServerError(
151
157
  `No browserWSEndpoint found, did you launch first?`,
@@ -179,4 +185,5 @@ export class ChromiumPlaywright extends EventEmitter {
179
185
  },
180
186
  );
181
187
  });
188
+ }
182
189
  }
@@ -42,13 +42,15 @@ export class FirefoxPlaywright extends EventEmitter {
42
42
  this.removeAllListeners();
43
43
  }
44
44
 
45
- public keepAlive() {
46
- return false;
45
+ public keepUntil() {
46
+ return 0;
47
47
  }
48
48
 
49
- public isRunning = (): boolean => this.running;
49
+ public isRunning(): boolean {
50
+ return this.running;
51
+ }
50
52
 
51
- public close = async (): Promise<void> => {
53
+ public async close(): Promise<void> {
52
54
  if (this.browser) {
53
55
  this.logger.trace(
54
56
  `Closing ${this.constructor.name} process and all listeners`,
@@ -60,33 +62,34 @@ export class FirefoxPlaywright extends EventEmitter {
60
62
  this.browser = null;
61
63
  this.browserWSEndpoint = null;
62
64
  }
63
- };
65
+ }
64
66
 
65
- public pages = async (): Promise<[]> => [];
67
+ public async pages(): Promise<[]> {
68
+ return [];
69
+ }
66
70
 
67
- public getPageId = (): string => {
71
+ public getPageId(): string {
68
72
  throw new ServerError(
69
73
  `#getPageId is not yet supported with ${this.constructor.name}.`,
70
74
  );
71
- };
75
+ }
72
76
 
73
- public makeLiveURL = (): void => {
77
+ public makeLiveURL(): void {
74
78
  throw new ServerError(
75
79
  `Live URLs are not yet supported with ${this.constructor.name}.`,
76
80
  );
77
- };
81
+ }
78
82
 
79
- public newPage = async (): Promise<Page> => {
83
+ public async newPage(): Promise<Page> {
80
84
  throw new ServerError(
81
85
  `Can't create new page with ${this.constructor.name}`,
82
86
  );
83
- };
87
+ }
84
88
 
85
- public launch = async (
89
+ public async launch(
86
90
  options: BrowserServerOptions = {},
87
91
  version?: string,
88
- ): Promise<playwright.BrowserServer> => {
89
-
92
+ ): Promise<playwright.BrowserServer> {
90
93
  this.logger.info(`Launching ${this.constructor.name} Handler`);
91
94
  const opts = {
92
95
  ...options,
@@ -109,11 +112,13 @@ export class FirefoxPlaywright extends EventEmitter {
109
112
  this.running = true;
110
113
 
111
114
  return this.browser;
112
- };
115
+ }
113
116
 
114
- public wsEndpoint = (): string | null => this.browserWSEndpoint;
117
+ public wsEndpoint(): string | null {
118
+ return this.browserWSEndpoint;
119
+ }
115
120
 
116
- public publicWSEndpoint = (token: string | null): string | null => {
121
+ public publicWSEndpoint(token: string | null): string | null {
117
122
  if (!this.browserWSEndpoint) {
118
123
  return null;
119
124
  }
@@ -128,18 +133,18 @@ export class FirefoxPlaywright extends EventEmitter {
128
133
  }
129
134
 
130
135
  return externalURL.href;
131
- };
136
+ }
132
137
 
133
- public proxyPageWebSocket = async () => {
138
+ public async proxyPageWebSocket() {
134
139
  this.logger.warn(`Not yet implemented in ${this.constructor.name}`);
135
- };
140
+ }
136
141
 
137
- public proxyWebSocket = async (
142
+ public async proxyWebSocket(
138
143
  req: Request,
139
144
  socket: Duplex,
140
145
  head: Buffer,
141
- ): Promise<void> =>
142
- new Promise((resolve, reject) => {
146
+ ): Promise<void> {
147
+ return new Promise((resolve, reject) => {
143
148
  if (!this.browserWSEndpoint) {
144
149
  throw new ServerError(
145
150
  `No browserWSEndpoint found, did you launch first?`,
@@ -173,4 +178,5 @@ export class FirefoxPlaywright extends EventEmitter {
173
178
  },
174
179
  );
175
180
  });
181
+ }
176
182
  }
@@ -37,7 +37,7 @@ import path from 'path';
37
37
 
38
38
  export class BrowserManager {
39
39
  protected browsers: Map<BrowserInstance, BrowserlessSession> = new Map();
40
- protected timers: Map<string, number> = new Map();
40
+ protected timers: Map<string, NodeJS.Timeout> = new Map();
41
41
  protected log = new Logger('browser-manager');
42
42
  protected chromeBrowsers = [ChromiumCDP, ChromeCDP];
43
43
  protected playwrightBrowserNames = [
@@ -52,10 +52,13 @@ export class BrowserManager {
52
52
  protected hooks: Hooks,
53
53
  ) {}
54
54
 
55
- protected browserIsChrome = (b: BrowserInstance) =>
56
- this.chromeBrowsers.some((chromeBrowser) => b instanceof chromeBrowser);
55
+ protected browserIsChrome(b: BrowserInstance) {
56
+ return this.chromeBrowsers.some(
57
+ (chromeBrowser) => b instanceof chromeBrowser,
58
+ );
59
+ }
57
60
 
58
- protected removeUserDataDir = async (userDataDir: string | null) => {
61
+ protected async removeUserDataDir(userDataDir: string | null) {
59
62
  if (userDataDir && (await exists(userDataDir))) {
60
63
  this.log.info(`Deleting data directory "${userDataDir}"`);
61
64
  await deleteAsync(userDataDir, { force: true }).catch((err) => {
@@ -64,18 +67,18 @@ export class BrowserManager {
64
67
  );
65
68
  });
66
69
  }
67
- };
70
+ }
68
71
 
69
- protected onNewPage = async (req: Request, page: Page) => {
70
- await this.hooks.page({ meta: req.parsed, page });
71
- };
72
+ protected async onNewPage(req: Request, page: Page) {
73
+ return await this.hooks.page({ meta: req.parsed, page });
74
+ }
72
75
 
73
76
  /**
74
77
  * Returns the /json/protocol API contents from Chromium or Chrome, whichever is installed,
75
78
  * and modifies URLs to set them to the appropriate addresses configured.
76
79
  * When both Chrome and Chromium are installed, defaults to Chromium.
77
80
  */
78
- public getProtocolJSON = async (logger: Logger): Promise<object> => {
81
+ public async getProtocolJSON(logger: Logger): Promise<object> {
79
82
  const Browser = (await availableBrowsers).find((InstalledBrowser) =>
80
83
  this.chromeBrowsers.some(
81
84
  (ChromeBrowser) => InstalledBrowser === ChromeBrowser,
@@ -104,14 +107,14 @@ export class BrowserManager {
104
107
  browser.close();
105
108
 
106
109
  return protocolJSON;
107
- };
110
+ }
108
111
 
109
112
  /**
110
113
  * Returns the /json/version API from Chromium or Chrome, whichever is installed,
111
114
  * and modifies URLs to set them to the appropriate addresses configured.
112
115
  * When both Chrome and Chromium are installed, defaults to Chromium.
113
116
  */
114
- public getVersionJSON = async (logger: Logger): Promise<CDPJSONPayload> => {
117
+ public async getVersionJSON(logger: Logger): Promise<CDPJSONPayload> {
115
118
  this.log.info(`Launching Chromium to generate /json/version results`);
116
119
  const Browser = (await availableBrowsers).find((InstalledBrowser) =>
117
120
  this.chromeBrowsers.some(
@@ -149,14 +152,14 @@ export class BrowserManager {
149
152
  'Debugger-Version': debuggerVersion,
150
153
  webSocketDebuggerUrl: this.config.getExternalWebSocketAddress(),
151
154
  };
152
- };
155
+ }
153
156
 
154
157
  /**
155
158
  * Returns a list of all Chrome-like browsers (both Chromium and Chrome) with
156
159
  * their respective /json/list contents. URLs are modified so that subsequent
157
160
  * calls can be forwarded to the appropriate destination
158
161
  */
159
- public getJSONList = async (): Promise<Array<CDPJSONPayload>> => {
162
+ public async getJSONList(): Promise<Array<CDPJSONPayload>> {
160
163
  const externalAddress = this.config.getExternalWebSocketAddress();
161
164
  const externalURL = new URL(externalAddress);
162
165
  const sessions = Array.from(this.browsers);
@@ -214,19 +217,19 @@ export class BrowserManager {
214
217
  return cdpResponse
215
218
  .flat()
216
219
  .filter((_) => _ !== null) as Array<CDPJSONPayload>;
217
- };
220
+ }
218
221
 
219
- protected generateSessionJson = async (
222
+ protected async generateSessionJson(
220
223
  browser: BrowserInstance,
221
224
  session: BrowserlessSession,
222
- ) => {
225
+ ) {
223
226
  const serverAddress = this.config.getExternalAddress();
224
227
 
225
228
  const sessions = [
226
229
  {
227
230
  ...session,
228
231
  browser: browser.constructor.name,
229
- browserId: browser.wsEndpoint()?.split('/').pop() as string,
232
+ browserId: session.id,
230
233
  initialConnectURL: new URL(session.initialConnectURL, serverAddress)
231
234
  .href,
232
235
  killURL: session.id
@@ -262,35 +265,63 @@ export class BrowserManager {
262
265
  }
263
266
  }
264
267
  return sessions;
265
- };
268
+ }
266
269
 
267
- public close = async (
270
+ public async close(
268
271
  browser: BrowserInstance,
269
272
  session: BrowserlessSession,
270
- ): Promise<void> => {
273
+ ): Promise<void> {
274
+ const now = Date.now();
275
+ const keepUntil = browser.keepUntil();
276
+ const connected = session.numbConnected;
277
+ const hasKeepUntil = keepUntil > now;
278
+ const keepOpen = connected > 0 || hasKeepUntil;
271
279
  const cleanupACtions: Array<() => Promise<void>> = [];
272
- this.log.info(`${session.numbConnected} Client(s) are currently connected`);
280
+ const priorTimer = this.timers.get(session.id);
273
281
 
274
- // Don't close if there's clients still connected
275
- if (session.numbConnected > 0 || browser.keepAlive()) {
276
- return;
282
+ if (priorTimer) {
283
+ this.log.info(`Deleting prior keep-until timer for "${session.id}"`);
284
+ global.clearTimeout(priorTimer);
277
285
  }
278
286
 
279
- this.log.info(`Closing browser session`);
280
- cleanupACtions.push(() => browser.close());
287
+ this.log.info(
288
+ `${session.numbConnected} Client(s) are currently connected, Keep-until: ${keepUntil}`,
289
+ );
281
290
 
282
- if (session.isTempDataDir) {
283
- this.log.info(
284
- `Deleting "${session.userDataDir}" user-data-dir and session from memory`,
291
+ if (hasKeepUntil) {
292
+ const timeout = keepUntil - now;
293
+ this.log.trace(
294
+ `Setting timer ${timeout.toLocaleString()} for "${session.id}"`,
295
+ );
296
+ this.timers.set(
297
+ session.id,
298
+ global.setTimeout(() => {
299
+ const session = this.browsers.get(browser);
300
+ if (session) {
301
+ this.log.trace(`Timer hit for "${session.id}"`),
302
+ this.close(browser, session);
303
+ }
304
+ }, timeout),
285
305
  );
286
- this.browsers.delete(browser);
287
- cleanupACtions.push(() => this.removeUserDataDir(session.userDataDir));
288
306
  }
289
307
 
290
- await Promise.all(cleanupACtions.map((a) => a()));
291
- };
308
+ if (!keepOpen) {
309
+ this.log.info(`Closing browser session`);
310
+ cleanupACtions.push(() => browser.close());
292
311
 
293
- public getAllSessions = async (): Promise<BrowserlessSessionJSON[]> => {
312
+ if (session.isTempDataDir) {
313
+ this.log.info(
314
+ `Deleting "${session.userDataDir}" user-data-dir and session from memory`,
315
+ );
316
+ this.browsers.delete(browser);
317
+ cleanupACtions.push(() => this.removeUserDataDir(session.userDataDir));
318
+ }
319
+
320
+ await Promise.all(cleanupACtions.map((a) => a()));
321
+ }
322
+ }
323
+
324
+ public async getAllSessions(): Promise<BrowserlessSessionJSON[]> {
294
325
  const sessions = Array.from(this.browsers);
295
326
 
296
327
  const formattedSessions: BrowserlessSessionJSON[] = [];
@@ -299,9 +330,9 @@ export class BrowserManager {
299
330
  formattedSessions.push(...formattedSession);
300
331
  }
301
332
  return formattedSessions;
302
- };
333
+ }
303
334
 
304
- public complete = async (browser: BrowserInstance): Promise<void> => {
335
+ public async complete(browser: BrowserInstance): Promise<void> {
305
336
  const session = this.browsers.get(browser);
306
337
  if (!session) {
307
338
  this.log.info(
@@ -319,13 +350,13 @@ export class BrowserManager {
319
350
  --session.numbConnected;
320
351
 
321
352
  this.close(browser, session);
322
- };
353
+ }
323
354
 
324
- public getBrowserForRequest = async (
355
+ public async getBrowserForRequest(
325
356
  req: Request,
326
357
  router: BrowserHTTPRoute | BrowserWebsocketRoute,
327
358
  logger: Logger,
328
- ): Promise<BrowserInstance> => {
359
+ ): Promise<BrowserInstance> {
329
360
  const { browser: Browser } = router;
330
361
  const blockAds = parseBooleanParam(
331
362
  req.parsed.searchParams,
@@ -459,8 +490,14 @@ export class BrowserManager {
459
490
  userDataDir,
460
491
  });
461
492
 
493
+ const match = (req.headers['user-agent'] || '').match(pwVersionRegex);
494
+ const pwVersion = match ? match[1] : 'default';
495
+
496
+ await browser.launch(launchOptions as object, pwVersion);
497
+ await this.hooks.browser({ browser, meta: req.parsed });
498
+
462
499
  const session: BrowserlessSession = {
463
- id: null,
500
+ id: browser.wsEndpoint()?.split('/').pop() as string,
464
501
  initialConnectURL:
465
502
  path.join(req.parsed.pathname, req.parsed.search) || '',
466
503
  isTempDataDir: !manualUserDataDir,
@@ -475,21 +512,15 @@ export class BrowserManager {
475
512
 
476
513
  this.browsers.set(browser, session);
477
514
 
478
- const match = (req.headers['user-agent'] || '').match(pwVersionRegex);
479
- const pwVersion = match ? match[1] : 'default';
480
-
481
- await browser.launch(launchOptions as object, pwVersion);
482
- await this.hooks.browser({ browser, meta: req.parsed });
483
-
484
515
  browser.on('newPage', async (page) => {
485
516
  await this.onNewPage(req, page);
486
517
  (router.onNewPage || noop)(req.parsed || '', page);
487
518
  });
488
519
 
489
520
  return browser;
490
- };
521
+ }
491
522
 
492
- public shutdown = async (): Promise<void> => {
523
+ public async shutdown(): Promise<void> {
493
524
  this.log.info(`Closing down browser instances`);
494
525
  const sessions = Array.from(this.browsers);
495
526
  await Promise.all(sessions.map(([b]) => b.close()));
@@ -500,10 +531,10 @@ export class BrowserManager {
500
531
  this.timers = new Map();
501
532
  await this.stop();
502
533
  this.log.info(`Shutdown complete`);
503
- };
534
+ }
504
535
 
505
536
  /**
506
537
  * Left blank for downstream SDK modules to optionally implement.
507
538
  */
508
- public stop = () => {};
539
+ public stop() {}
509
540
  }