@browserless/screenshot 13.1.6 → 13.1.7

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 (2) hide show
  1. package/package.json +5 -4
  2. package/src/index.js +32 -8
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@browserless/screenshot",
3
3
  "description": "Capture high-quality screenshots of websites with overlay support, device emulation, and automated image optimization.",
4
4
  "homepage": "https://browserless.js.org/#/?id=screenshoturl-options",
5
- "version": "13.1.6",
5
+ "version": "13.1.7",
6
6
  "main": "src/index.js",
7
7
  "author": {
8
8
  "email": "hello@microlink.io",
@@ -32,7 +32,8 @@
32
32
  "web-capture"
33
33
  ],
34
34
  "dependencies": {
35
- "@browserless/goto": "13.1.6",
35
+ "@browserless/errors": "13.1.7",
36
+ "@browserless/goto": "13.1.7",
36
37
  "@kikobeats/content-type": "~1.0.4",
37
38
  "@kikobeats/time-span": "~1.0.12",
38
39
  "automad-prism-themes": "~0.3.7",
@@ -51,7 +52,7 @@
51
52
  "svg-gradient": "~1.0.4"
52
53
  },
53
54
  "devDependencies": {
54
- "@browserless/test": "13.1.6",
55
+ "@browserless/test": "13.1.7",
55
56
  "ava": "5"
56
57
  },
57
58
  "engines": {
@@ -70,5 +71,5 @@
70
71
  "timeout": "2m",
71
72
  "workerThreads": false
72
73
  },
73
- "gitHead": "6e72f371de1e8515b309b9a22e7632a564af27b0"
74
+ "gitHead": "cb9a0c6fc11176da209ba9bc33bca5a47c3aabbe"
74
75
  }
package/src/index.js CHANGED
@@ -1,18 +1,34 @@
1
1
  'use strict'
2
2
 
3
3
  const debug = require('debug-logfmt')('browserless:screenshot')
4
+ const { isContextDestroyed } = require('@browserless/errors')
4
5
  const createGoto = require('@browserless/goto')
5
6
  const pReflect = require('p-reflect')
6
7
 
7
8
  const isWhiteScreenshot = require('./is-white-screenshot')
8
9
  const waitForPrism = require('./pretty')
9
- const timeSpan = require('./time-span')
10
+ const prettyTimeSpan = require('./time-span')
10
11
  const overlay = require('./overlay')
11
12
  const { waitForDomStability, resolveWaitForDom, DEFAULT_WAIT_FOR_DOM } = require('./wait-for-dom')
12
13
 
13
- const createElapsed = () => {
14
- const start = Date.now()
15
- return () => Date.now() - start
14
+ const timeSpan = require('@kikobeats/time-span')()
15
+
16
+ // Retry a page capture (screenshot/pdf) that races with a client-side
17
+ // navigation. When the execution context is destroyed mid-capture, the page is
18
+ // navigating: wait for it to settle via `waitUntilAuto` and retry in-place,
19
+ // bounded by `timeout`, rather than failing the whole request. SPAs (e.g.
20
+ // scribd) navigate client-side after load, so the initial capture often races.
21
+ const captureWithNavigationRetry = async (capture, { page, goto, timeout }) => {
22
+ const elapsed = timeSpan()
23
+ while (true) {
24
+ try {
25
+ return await capture()
26
+ } catch (error) {
27
+ if (!isContextDestroyed(error) || elapsed() >= timeout) throw error
28
+ debug('captureWithNavigationRetry', { error: error.message })
29
+ await goto.waitUntilAuto(page, { timeout })
30
+ }
31
+ }
16
32
  }
17
33
 
18
34
  const getPageSnapshot = page =>
@@ -170,13 +186,17 @@ module.exports = ({ goto, ...gotoOpts }) => {
170
186
 
171
187
  const takeScreenshot = async opts => {
172
188
  const timeout = goto.timeouts.action(opts.timeout)
173
- const elapsed = createElapsed()
189
+ const elapsed = timeSpan()
174
190
  let retry = 0
175
191
  let isWhite = false
176
192
  let isReady = false
177
193
 
178
194
  do {
179
- screenshot = await page.screenshot(opts)
195
+ screenshot = await captureWithNavigationRetry(() => page.screenshot(opts), {
196
+ page,
197
+ goto,
198
+ timeout
199
+ })
180
200
  isWhite = await isWhiteScreenshot(screenshot)
181
201
  const snapshotResult = await pReflect(getPageSnapshot(page))
182
202
  const pageSnapshot = snapshotResult.isRejected ? {} : snapshotResult.value
@@ -205,12 +225,15 @@ module.exports = ({ goto, ...gotoOpts }) => {
205
225
  page.on('dialog', onDialog)
206
226
 
207
227
  try {
208
- const timeScreenshot = timeSpan()
228
+ const timeScreenshot = prettyTimeSpan()
209
229
 
210
230
  if (waitUntil !== 'auto') {
211
231
  ;({ response } = await goto(page, { ...opts, url, waitUntil }))
212
232
  const screenshotOpts = await beforeScreenshot(page, response, opts)
213
- screenshot = await page.screenshot({ ...opts, ...screenshotOpts })
233
+ screenshot = await captureWithNavigationRetry(
234
+ () => page.screenshot({ ...opts, ...screenshotOpts }),
235
+ { page, goto, timeout: goto.timeouts.action(opts.timeout) }
236
+ )
214
237
  debug('screenshot', { waitUntil, duration: timeScreenshot() })
215
238
  } else {
216
239
  ;({ response } = await goto(page, { ...opts, url, waitUntil, waitUntilAuto }))
@@ -242,6 +265,7 @@ module.exports = ({ goto, ...gotoOpts }) => {
242
265
  }
243
266
  }
244
267
 
268
+ module.exports.captureWithNavigationRetry = captureWithNavigationRetry
245
269
  module.exports.isWhiteScreenshot = isWhiteScreenshot
246
270
  module.exports.waitForDomStability = waitForDomStability
247
271
  module.exports.resolveWaitForDom = resolveWaitForDom