@browserless/goto 13.0.9 → 13.1.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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/package.json +4 -4
  3. package/src/index.js +66 -13
package/README.md CHANGED
@@ -71,6 +71,7 @@ const { response, device, error } = await goto(page, {
71
71
  | `javascript` | `boolean` | `true` | Enable/disable JavaScript |
72
72
  | `animations` | `boolean` | `false` | Enable CSS animations |
73
73
  | `colorScheme` | `string` | — | `'light'` or `'dark'` preference |
74
+ | `flattenShadowDOM` | `boolean` | `false` (`true` for `html()`) | Serialize open shadow DOM into HTML (mutates the page DOM; closed shadow roots are skipped) |
74
75
  | `mediaType` | `string` | — | CSS media type (`'screen'`, `'print'`) |
75
76
  | `timezone` | `string` | — | Timezone to emulate |
76
77
  | `authenticate` | `object` | — | HTTP authentication credentials |
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@browserless/goto",
3
3
  "description": "Navigate to web pages with built-in ad blocking, device emulation, and optimized loading for faster automation.",
4
4
  "homepage": "https://browserless.js.org/#/?id=gotopage-options",
5
- "version": "13.0.9",
5
+ "version": "13.1.1",
6
6
  "main": "src/index.js",
7
7
  "author": {
8
8
  "email": "hello@microlink.io",
@@ -31,7 +31,7 @@
31
31
  ],
32
32
  "dependencies": {
33
33
  "@browserless/devices": "13.0.0",
34
- "@duckduckgo/autoconsent": "~14.90.0",
34
+ "@duckduckgo/autoconsent": "~14.92.0",
35
35
  "@ghostery/adblocker-puppeteer": "~2.18.0",
36
36
  "debug-logfmt": "~1.4.10",
37
37
  "got": "~11.8.6",
@@ -42,7 +42,7 @@
42
42
  "tough-cookie": "~6.0.1"
43
43
  },
44
44
  "devDependencies": {
45
- "@browserless/test": "13.0.9",
45
+ "@browserless/test": "13.1.1",
46
46
  "ava": "5",
47
47
  "p-wait-for": "3"
48
48
  },
@@ -66,5 +66,5 @@
66
66
  "timeout": "2m",
67
67
  "workerThreads": false
68
68
  },
69
- "gitHead": "b4a515a809d6d7d33828e901d18d32e805e26d55"
69
+ "gitHead": "d491c5204deef804c923539063d92658f4e9649d"
70
70
  }
package/src/index.js CHANGED
@@ -228,18 +228,11 @@ module.exports = ({ defaultDevice = 'Macbook Pro 13', timeout: globalTimeout, ..
228
228
 
229
229
  // related https://github.com/puppeteer/puppeteer/issues/1353
230
230
  const _waitUntilAuto = (page, { timeout }) => {
231
- return Promise.all(
232
- [
233
- {
234
- fn: () => page.waitForNavigation({ waitUntil: 'networkidle2' }),
235
- debug: 'waitUntilAuto:waitForNavigation'
236
- },
237
- {
238
- fn: () => page.evaluate(() => window.history.pushState(null, null, null)),
239
- debug: 'waitUntilAuto:pushState'
240
- }
241
- ].map(({ fn, debug }) => run({ fn: fn(), debug, timeout }))
242
- )
231
+ return run({
232
+ fn: page.waitForNetworkIdle({ idleTime: 500, concurrency: 2 }),
233
+ debug: 'waitUntilAuto:networkIdle',
234
+ timeout
235
+ })
243
236
  }
244
237
 
245
238
  const goto = async (
@@ -251,6 +244,7 @@ module.exports = ({ defaultDevice = 'Macbook Pro 13', timeout: globalTimeout, ..
251
244
  authenticate,
252
245
  click,
253
246
  colorScheme,
247
+ flattenShadowDOM = false,
254
248
  headers: rawHeaders = {},
255
249
  html,
256
250
  javascript = true,
@@ -481,7 +475,7 @@ module.exports = ({ defaultDevice = 'Macbook Pro 13', timeout: globalTimeout, ..
481
475
  return Promise.race([page.goto(url, { waitUntil, ...args }), promise])
482
476
  })()
483
477
 
484
- const { value: response, reason: error } = await run({
478
+ let { value: response, reason: error } = await run({
485
479
  fn: navigationPromise,
486
480
  timeout: gotoTimeout,
487
481
  debug: { fn: html ? 'html' : 'url', waitUntil }
@@ -547,6 +541,65 @@ module.exports = ({ defaultDevice = 'Macbook Pro 13', timeout: globalTimeout, ..
547
541
  await waitUntilAuto(page, { response, timeout: actionTimeout * 2 })
548
542
  }
549
543
 
544
+ if (flattenShadowDOM) {
545
+ const { isRejected, reason: flattenError } = await run({
546
+ fn: page.evaluate(() => {
547
+ ;(function flatten (root) {
548
+ const replaceSlot = (slot, nodes) => {
549
+ const parent = slot.parentNode
550
+ if (!parent) return
551
+ for (const node of nodes) parent.insertBefore(node, slot)
552
+ slot.remove()
553
+ }
554
+
555
+ const flattenNode = node => {
556
+ if (node.nodeType !== window.Node.ELEMENT_NODE) return
557
+ if (node.shadowRoot) flattenElement(node)
558
+ flattenChildren(node)
559
+ }
560
+
561
+ const flattenChildren = root => {
562
+ for (const el of root.querySelectorAll('*')) {
563
+ if (el.shadowRoot) flattenElement(el)
564
+ if (el.localName !== 'slot') continue
565
+
566
+ const slot = el
567
+ const nodes = slot.assignedNodes({ flatten: true })
568
+ if (nodes.length > 0) {
569
+ const clones = nodes.map(node => {
570
+ flattenNode(node)
571
+ return node.cloneNode(true)
572
+ })
573
+
574
+ replaceSlot(slot, clones)
575
+ } else {
576
+ const fallback = [...slot.childNodes]
577
+ fallback.forEach(flattenNode)
578
+ if (fallback.length > 0) {
579
+ replaceSlot(
580
+ slot,
581
+ fallback.map(n => n.cloneNode(true))
582
+ )
583
+ } else slot.remove()
584
+ }
585
+ }
586
+ }
587
+
588
+ const flattenElement = el => {
589
+ flattenChildren(el.shadowRoot)
590
+ el.innerHTML = el.shadowRoot.innerHTML
591
+ }
592
+
593
+ flattenChildren(root)
594
+ })(document.body)
595
+ }),
596
+ timeout: actionTimeout,
597
+ debug: 'flattenShadowDOM'
598
+ })
599
+
600
+ if (isRejected) error = error || flattenError || new Error('flattenShadowDOM failed')
601
+ }
602
+
550
603
  return { response, device, error }
551
604
  } finally {
552
605
  if (abortTypesHandler) page.off('request', abortTypesHandler)