browserless 14.1.2 → 14.1.4

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 (3) hide show
  1. package/index.d.ts +4 -1
  2. package/package.json +5 -5
  3. package/src/index.js +28 -17
package/index.d.ts CHANGED
@@ -40,7 +40,10 @@ export interface Context {
40
40
  text: (url: string, opts?: GotoOptions) => Promise<string>
41
41
  getDevice: (deviceName: string) => Viewport | undefined
42
42
  report: (opts?: { benchmark?: boolean }) => Promise<HardwareInfo>
43
- destroyContext: (opts?: { force?: boolean }) => Promise<void>
43
+ destroyContext: (opts?: {
44
+ /** @deprecated A destroyed context never retries, so `force` has no effect. */
45
+ force?: boolean
46
+ }) => Promise<void>
44
47
  withPage: <T>(fn: (page: Page, goto: unknown) => Promise<T>, opts?: { timeout?: number, keepPage?: boolean }) => Promise<T>
45
48
  }
46
49
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "browserless",
3
3
  "description": "The headless Chrome/Chromium driver on top of Puppeteer. Take screenshots, generate PDFs, extract text and HTML with a production-ready API.",
4
4
  "homepage": "https://browserless.js.org",
5
- "version": "14.1.2",
5
+ "version": "14.1.4",
6
6
  "types": "index.d.ts",
7
7
  "main": "src/index.js",
8
8
  "author": {
@@ -36,9 +36,9 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@browserless/errors": "14.0.22",
39
- "@browserless/goto": "14.1.2",
40
- "@browserless/pdf": "14.1.2",
41
- "@browserless/screenshot": "14.1.2",
39
+ "@browserless/goto": "14.1.4",
40
+ "@browserless/pdf": "14.1.4",
41
+ "@browserless/screenshot": "14.1.4",
42
42
  "debug-logfmt": "~1.4.15",
43
43
  "kill-process-group": "~1.0.16",
44
44
  "p-reflect": "~2.1.0",
@@ -70,5 +70,5 @@
70
70
  "timeout": "2m",
71
71
  "workerThreads": false
72
72
  },
73
- "gitHead": "700bbabadea0a16facf30d76ecf60ff9602e06f8"
73
+ "gitHead": "136b2d9f50772f096aa81af66780c1b625ec2fc1"
74
74
  }
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { ensureError, browserTimeout } = require('@browserless/errors')
3
+ const { ensureError, browserTimeout, contextDisconnected } = require('@browserless/errors')
4
4
  const createScreenshot = require('@browserless/screenshot')
5
5
  const debug = require('debug-logfmt')('browserless')
6
6
  const createGoto = require('@browserless/goto')
@@ -111,18 +111,23 @@ module.exports = ({ timeout: globalTimeout = 30000, ...launchOpts } = {}) => {
111
111
  }
112
112
 
113
113
  const createContext = async ({ retry = 2, timeout: contextTimeout, ...contextOpts } = {}) => {
114
- const forwardedContextOpts = isClosed
115
- ? Promise.resolve(contextOpts)
116
- : forwardProxy(contextOpts, { mode: launchOpts.mode })
117
- forwarders.add(forwardedContextOpts)
118
- const newBrowserContext = () => forwardedContextOpts.then(createBrowserContext)
119
- const usesForwarder = () => forwardedContextOpts.then(opts => opts !== contextOpts)
114
+ let forwardedContextOpts
115
+ const forwardContextOpts = () => {
116
+ forwardedContextOpts = isClosed
117
+ ? Promise.resolve(contextOpts)
118
+ : forwardProxy(contextOpts, { mode: launchOpts.mode })
119
+ forwarders.add(forwardedContextOpts)
120
+ return forwardedContextOpts
121
+ }
122
+ const newBrowserContext = () =>
123
+ (forwardedContextOpts ?? forwardContextOpts()).then(createBrowserContext)
120
124
  let _contextPromise = newBrowserContext()
121
- let isDestroyedForced = false
122
125
  let isDestroyed = false
123
126
  const pageMetadata = new WeakMap()
127
+ const isAbandoned = () => isClosed || isDestroyed
124
128
 
125
- const getBrowserContext = () => _contextPromise
129
+ const getBrowserContext = () =>
130
+ isDestroyed ? Promise.reject(contextDisconnected()) : _contextPromise
126
131
 
127
132
  const createPage = async name => {
128
133
  const duration = debug.duration('createPage')
@@ -172,9 +177,11 @@ module.exports = ({ timeout: globalTimeout = 30000, ...launchOpts } = {}) => {
172
177
  }
173
178
 
174
179
  try {
180
+ if (isAbandoned()) throw contextDisconnected()
175
181
  page = await createPage(name)
176
182
  closePageTimeout = startCloseTimeout()
177
183
  const value = await fn(page, goto)(...args)
184
+ if (isAbandoned()) throw contextDisconnected()
178
185
  // A timeout already rejected the caller, who never received this
179
186
  // page. Retaining it would clear the in-call watchdog and arm a
180
187
  // new one with nobody holding the page.
@@ -200,11 +207,11 @@ module.exports = ({ timeout: globalTimeout = 30000, ...launchOpts } = {}) => {
200
207
  onFailedAttempt: async error => {
201
208
  debug('onFailedAttempt', { name: error.name, code: error.code, isRejected })
202
209
  if (error.name === 'AbortError') throw error
203
- if (isRejected || isDestroyedForced) throw new AbortError()
210
+ if (isRejected) throw new AbortError()
204
211
  const isRetryable =
205
212
  error.code === 'EBRWSRCONTEXTCONNRESET' || error.code === 'EPROTOCOL'
206
213
  if (!isRetryable) throw error
207
- if (isDestroyed && (await usesForwarder())) throw new AbortError()
214
+ if (isAbandoned()) throw new AbortError()
208
215
  const { message, attemptNumber, retriesLeft } = error
209
216
  // Every attempt builds its own page, so both codes are retryable
210
217
  // inside the existing context: `EBRWSRCONTEXTCONNRESET` covers a
@@ -247,20 +254,24 @@ module.exports = ({ timeout: globalTimeout = 30000, ...launchOpts } = {}) => {
247
254
  )
248
255
  }
249
256
 
250
- const destroyContext = async ({ force = false } = {}) => {
257
+ const destroyContext = async () => {
251
258
  isDestroyed = true
252
- if (force) isDestroyedForced = true
259
+ const closingContextPromise = _contextPromise
260
+ const closingContextOpts = forwardedContextOpts
261
+ _contextPromise = undefined
262
+ forwardedContextOpts = undefined
263
+ if (!closingContextPromise) return
253
264
 
254
265
  try {
255
266
  const [browserProcess, browserContext] = await Promise.all([
256
267
  getBrowser(),
257
- getBrowserContext()
268
+ closingContextPromise
258
269
  ])
259
270
  await pReflect(browserContext.close())
260
- debug('destroyContext', { pid: driver.pid(browserProcess), force, id: browserContext.id })
271
+ debug('destroyContext', { pid: driver.pid(browserProcess), id: browserContext.id })
261
272
  } finally {
262
- forwarders.delete(forwardedContextOpts)
263
- await closeForwarder(forwardedContextOpts)
273
+ forwarders.delete(closingContextOpts)
274
+ await closeForwarder(closingContextOpts)
264
275
  }
265
276
  }
266
277