browserless 9.3.14-alpha.0 → 9.3.14-alpha.3

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/package.json +5 -6
  2. package/src/driver.js +13 -18
  3. package/src/index.js +29 -14
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "browserless",
3
3
  "description": "the headless Chrome/Chromium performance driver for Node.js",
4
4
  "homepage": "https://browserless.js.org",
5
- "version": "9.3.14-alpha.0",
5
+ "version": "9.3.14-alpha.3",
6
6
  "main": "src/index.js",
7
7
  "author": {
8
8
  "email": "hello@microlink.io",
@@ -32,15 +32,14 @@
32
32
  ],
33
33
  "dependencies": {
34
34
  "@browserless/errors": "^9.3.0",
35
- "@browserless/goto": "^9.3.14-alpha.0",
36
- "@browserless/pdf": "^9.3.14-alpha.0",
37
- "@browserless/screenshot": "^9.3.14-alpha.0",
35
+ "@browserless/goto": "^9.3.14-alpha.1",
36
+ "@browserless/pdf": "^9.3.14-alpha.1",
37
+ "@browserless/screenshot": "^9.3.14-alpha.2",
38
38
  "debug-logfmt": "~1.0.4",
39
39
  "mutexify": "~1.4.0",
40
40
  "p-reflect": "~2.1.0",
41
41
  "p-retry": "~4.6.1",
42
42
  "p-timeout": "~4.1.0",
43
- "pidtree": "~0.5.0",
44
43
  "require-one-of": "~1.0.15"
45
44
  },
46
45
  "devDependencies": {
@@ -61,5 +60,5 @@
61
60
  "timeout": "2m",
62
61
  "verbose": true
63
62
  },
64
- "gitHead": "fe55af014debe242ee3823464a5e760afafca821"
63
+ "gitHead": "331f2fdf8f3de1c9534451b2adc36dc9c0e936f6"
65
64
  }
package/src/driver.js CHANGED
@@ -3,7 +3,9 @@
3
3
  const debug = require('debug-logfmt')('browserless')
4
4
  const requireOneOf = require('require-one-of')
5
5
  const pReflect = require('p-reflect')
6
- const pidtree = require('pidtree')
6
+ const { promisify } = require('util')
7
+
8
+ const exec = promisify(require('child_process').exec)
7
9
 
8
10
  // flags explained: https://peter.sh/experiments/chromium-command-line-switches
9
11
  // default flags: https://github.com/puppeteer/puppeteer/blob/edb01972b9606d8b05b979a588eda0d622315981/src/node/Launcher.ts#L183
@@ -58,30 +60,23 @@ const getPid = childProcess => {
58
60
  return browserProcess.pid
59
61
  }
60
62
 
61
- const getPids = async pid => {
62
- const { value: pids = [] } = await pReflect(pidtree(pid))
63
- return pids.includes(pid) ? pids : [...pids, pid]
64
- }
63
+ const killProcesssGroupPID = (pid, signal) =>
64
+ process.platform === 'win32'
65
+ ? exec(`taskkill /pid ${this.proc.pid} /T /F`)
66
+ : Promise.resolve(process.kill(-pid, signal))
65
67
 
66
68
  const close = async (childProcess, { signal = 'SIGKILL', ...debugOpts } = {}) => {
67
69
  const pid = getPid(childProcess)
68
70
  if (!pid) return
69
71
 
70
- const pids = await getPids(pid)
71
- pids.forEach(pid => {
72
- try {
73
- process.kill(pid, signal)
74
- } catch (_) {}
75
- })
76
-
77
72
  // It's necessary to call `browser.close` for removing temporal files associated
78
- // and remove listeners attached to the main process
79
- // see https://github.com/puppeteer/puppeteer/blob/778ac92469d66c542c3c12fe0aa23703dd6315c2/src/node/BrowserRunner.ts#L146
80
- if (childProcess.close) await childProcess.close()
81
-
82
- debug('close', { pids, signal, ...debugOpts })
73
+ // and remove listeners attached to the main process; check
74
+ // - https://github.com/puppeteer/puppeteer/blob/778ac92469d66c542c3c12fe0aa23703dd6315c2/src/node/BrowserRunner.ts#L146
75
+ // - https://github.com/puppeteer/puppeteer/blob/69d85e874416d62de6e821bef30e5cebcfd42f15/src/node/BrowserRunner.ts#L189
76
+ await pReflect(childProcess.close ? childProcess.close() : killProcesssGroupPID(pid, signal))
83
77
 
84
- return { pids }
78
+ debug('close', { pid, signal, ...debugOpts })
79
+ return { pid }
85
80
  }
86
81
 
87
82
  module.exports = { spawn, getPid, close, defaultArgs }
package/src/index.js CHANGED
@@ -46,10 +46,11 @@ module.exports = ({ timeout: globalTimeout = 30000, ...launchOpts } = {}) => {
46
46
 
47
47
  promise.then(async browser => {
48
48
  browser.once('disconnected', getBrowser)
49
+ const pid = driver.getPid(browser)
49
50
 
50
51
  debug('spawn', {
51
52
  respawn: isRespawn,
52
- pid: driver.getPid(browser) || launchOpts.mode,
53
+ pid: pid || launchOpts.mode,
53
54
  version: await browser.version()
54
55
  })
55
56
  })
@@ -80,26 +81,36 @@ module.exports = ({ timeout: globalTimeout = 30000, ...launchOpts } = {}) => {
80
81
  }
81
82
 
82
83
  const createContext = async ({ retry = 2, timeout: contextTimeout } = {}) => {
83
- let contextPromise = createBrowserContext()
84
+ let _contextPromise = createBrowserContext()
84
85
 
85
- contextPromise.then(context => {
86
+ const getBrowserContext = () => _contextPromise
87
+
88
+ getBrowserContext().then(context => {
86
89
  const browserProcess = context.browser()
87
90
  browserProcess.once('disconnected', async () => {
88
91
  await getBrowser()
89
- contextPromise = createBrowserContext()
92
+ _contextPromise = createBrowserContext()
90
93
  })
91
94
  })
92
95
 
93
96
  const createPage = async () => {
94
- const browserProcess = await getBrowser()
95
- const page = await (await contextPromise).newPage()
96
- debug('createPage', { pid: driver.getPid(browserProcess) })
97
+ const [browserProcess, browserContext] = await Promise.all([
98
+ getBrowser(),
99
+ getBrowserContext()
100
+ ])
101
+ const page = await browserContext.newPage()
102
+ debug('createPage', { pid: driver.getPid(browserProcess), id: browserContext._id })
97
103
  return page
98
104
  }
99
105
 
100
106
  const closePage = async page => {
101
107
  if (page && !page.isClosed()) {
102
- debug('closePage', await pReflect(page.close()))
108
+ const [browserProcess, browserContext] = await Promise.all([
109
+ getBrowser(),
110
+ getBrowserContext(),
111
+ pReflect(page.close())
112
+ ])
113
+ debug('closePage', { pid: driver.getPid(browserProcess), id: browserContext._id })
103
114
  }
104
115
  }
105
116
 
@@ -128,7 +139,7 @@ module.exports = ({ timeout: globalTimeout = 30000, ...launchOpts } = {}) => {
128
139
  debug('onFailedAttempt', { name: error.name, code: error.code, isRejected })
129
140
  if (error.name === 'AbortError') throw error
130
141
  if (isRejected) throw new AbortError()
131
- if (error.code === 'EBRWSRCONTEXTCONNRESET') contextPromise = createBrowserContext()
142
+ if (error.code === 'EBRWSRCONTEXTCONNRESET') _contextPromise = createBrowserContext()
132
143
  const { message, attemptNumber, retriesLeft } = error
133
144
  debug('retry', { attemptNumber, retriesLeft, message })
134
145
  }
@@ -152,15 +163,19 @@ module.exports = ({ timeout: globalTimeout = 30000, ...launchOpts } = {}) => {
152
163
  )
153
164
 
154
165
  const destroyContext = async () => {
155
- const { isRejected, reason: error } = await pReflect(
156
- contextPromise.then(context => context.close())
157
- )
158
- debug('destroyContext', isRejected ? { error } : {})
166
+ const [browserProcess, browserContext] = await Promise.all([
167
+ getBrowser(),
168
+ getBrowserContext()
169
+ ])
170
+ const id = browserContext._id
171
+ await pReflect(browserContext.close())
172
+
173
+ debug('destroyContext', { pid: driver.getPid(browserProcess), id })
159
174
  }
160
175
 
161
176
  return {
162
177
  respawn,
163
- context: () => contextPromise,
178
+ context: getBrowserContext,
164
179
  browser: getBrowser,
165
180
  evaluate,
166
181
  goto,