@browserless/function 10.5.4 → 10.6.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.
- package/package.json +6 -12
- package/src/function.js +12 -41
- package/src/index.js +33 -52
- package/src/vm.js +0 -34
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@browserless/function",
|
|
3
3
|
"description": "Run abritrary JavaScript inside a browser sandbox",
|
|
4
4
|
"homepage": "https://browserless.js.org",
|
|
5
|
-
"version": "10.
|
|
5
|
+
"version": "10.6.1",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"author": {
|
|
8
8
|
"email": "hello@microlink.io",
|
|
@@ -29,20 +29,14 @@
|
|
|
29
29
|
"serverless"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"
|
|
33
|
-
"debug-logfmt": "~1.2.1",
|
|
34
|
-
"deepmerge": "~4.3.1",
|
|
35
|
-
"p-reflect": "~2.1.0",
|
|
32
|
+
"isolated-function": "~0.1.4",
|
|
36
33
|
"p-retry": "~4.6.1",
|
|
37
|
-
"
|
|
38
|
-
"require-one-of": "~1.0.19",
|
|
39
|
-
"serialize-error": "~8.1.0",
|
|
40
|
-
"vm2": "~3.9.19"
|
|
34
|
+
"require-one-of": "~1.0.19"
|
|
41
35
|
},
|
|
42
36
|
"devDependencies": {
|
|
43
|
-
"@browserless/test": "^10.
|
|
37
|
+
"@browserless/test": "^10.6.0",
|
|
44
38
|
"ava": "5",
|
|
45
|
-
"browserless": "^10.
|
|
39
|
+
"browserless": "^10.6.0",
|
|
46
40
|
"lodash": "latest"
|
|
47
41
|
},
|
|
48
42
|
"engines": {
|
|
@@ -60,5 +54,5 @@
|
|
|
60
54
|
"timeout": "2m",
|
|
61
55
|
"workerThreads": false
|
|
62
56
|
},
|
|
63
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "44f9a1f22ea2020949e8d7465362908e3fd8e1a7"
|
|
64
58
|
}
|
package/src/function.js
CHANGED
|
@@ -1,51 +1,22 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const createVm = require('./vm')
|
|
6
|
-
|
|
7
|
-
const scriptPath = path.resolve(__dirname, 'function.js')
|
|
3
|
+
const isolatedFunction = require('isolated-function')
|
|
8
4
|
|
|
9
5
|
const createFn = code => `
|
|
10
|
-
async (
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const browserless = await getBrowserless({ mode: 'connect', browserWSEndpoint }).createContext()
|
|
15
|
-
const fnWrapper = fn => (page, response) => {
|
|
16
|
-
if (!response && opts.response) {
|
|
17
|
-
const { status, statusText, headers, html } = opts.response
|
|
18
|
-
response = {
|
|
19
|
-
ok: () => status === 0 || (status >= 200 && opts.status <= 299),
|
|
20
|
-
fromCache: () => false,
|
|
21
|
-
fromServiceWorker: () => false,
|
|
22
|
-
url: () => url,
|
|
23
|
-
text: () => html,
|
|
24
|
-
statusText: () => statusText,
|
|
25
|
-
json: () => JSON.parse(html),
|
|
26
|
-
headers: () => headers,
|
|
27
|
-
status: () => status
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return fn({ ...opts, page, response, url })
|
|
32
|
-
}
|
|
33
|
-
const browserFn = browserless.evaluate(fnWrapper(${code}), gotoOpts)
|
|
34
|
-
|
|
6
|
+
async (url, browserWSEndpoint, opts) => {
|
|
7
|
+
const puppeteer = require('@cloudflare/puppeteer')
|
|
8
|
+
const browser = await puppeteer.connect({ browserWSEndpoint })
|
|
9
|
+
const page = (await browser.pages())[1]
|
|
35
10
|
try {
|
|
36
|
-
|
|
37
|
-
return { isFulfilled: true, isRejected: false, value }
|
|
38
|
-
} catch (error) {
|
|
39
|
-
return { isFulfilled: false, isRejected: true, reason: serializeError(error) }
|
|
11
|
+
return await (${code})({ page, ...opts })
|
|
40
12
|
} finally {
|
|
41
|
-
await
|
|
42
|
-
await browserless.browser().then(browser => browser.disconnect())
|
|
13
|
+
await browser.disconnect()
|
|
43
14
|
}
|
|
44
15
|
}`
|
|
45
16
|
|
|
46
|
-
module.exports = ({ url, code, vmOpts,
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
return
|
|
17
|
+
module.exports = async ({ url, code, vmOpts, browserWSEndpoint, ...opts }) => {
|
|
18
|
+
const [fn, teardown] = isolatedFunction(createFn(code), { ...vmOpts, throwError: false })
|
|
19
|
+
const result = await fn(url, browserWSEndpoint, opts)
|
|
20
|
+
await teardown()
|
|
21
|
+
return result
|
|
51
22
|
}
|
package/src/index.js
CHANGED
|
@@ -1,60 +1,41 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const debug = require('debug-logfmt')('browserless:function')
|
|
3
|
+
const { isBrowserlessError, ensureError } = require('@browserless/errors')
|
|
5
4
|
const requireOneOf = require('require-one-of')
|
|
6
|
-
const pTimeout = require('p-timeout')
|
|
7
|
-
const pRetry = require('p-retry')
|
|
8
|
-
|
|
9
|
-
const { AbortError } = pRetry
|
|
10
|
-
|
|
11
5
|
const runFunction = require('./function')
|
|
12
6
|
|
|
13
7
|
const stringify = fn => fn.toString().trim().replace(/;$/, '')
|
|
14
8
|
|
|
15
|
-
module.exports =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
...
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
9
|
+
module.exports =
|
|
10
|
+
(
|
|
11
|
+
fn,
|
|
12
|
+
{
|
|
13
|
+
getBrowserless = requireOneOf(['browserless']),
|
|
14
|
+
retry = 2,
|
|
15
|
+
timeout = 30000,
|
|
16
|
+
gotoOpts,
|
|
17
|
+
...opts
|
|
18
|
+
} = {}
|
|
19
|
+
) =>
|
|
20
|
+
async (url, fnOpts = {}) => {
|
|
21
|
+
const browserlessPromise = getBrowserless()
|
|
22
|
+
const browser = await browserlessPromise
|
|
23
|
+
const browserless = await browser.createContext()
|
|
24
|
+
|
|
25
|
+
return browserless.withPage((page, goto) => async () => {
|
|
26
|
+
const { device } = await goto(page, { url, timeout, ...gotoOpts })
|
|
27
|
+
const result = await runFunction({
|
|
28
|
+
url,
|
|
29
|
+
code: stringify(fn),
|
|
30
|
+
browserWSEndpoint: (await browserless.browser()).wsEndpoint(),
|
|
31
|
+
device,
|
|
32
|
+
...opts,
|
|
33
|
+
...fnOpts
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
if (result.isFulfilled) return result
|
|
37
|
+
const error = ensureError(result.value)
|
|
38
|
+
if (isBrowserlessError(error)) throw error
|
|
39
|
+
return result
|
|
40
|
+
})()
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
const task = () =>
|
|
41
|
-
pRetry(run, {
|
|
42
|
-
retries: retry,
|
|
43
|
-
onFailedAttempt: async error => {
|
|
44
|
-
if (error.name === 'AbortError') throw error
|
|
45
|
-
if (isRejected) throw new AbortError()
|
|
46
|
-
await (await browserlessPromise).respawn()
|
|
47
|
-
const { message, attemptNumber, retriesLeft } = error
|
|
48
|
-
debug('retry', { attemptNumber, retriesLeft, message })
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
// main
|
|
53
|
-
const result = await pTimeout(task(), timeout, () => {
|
|
54
|
-
isRejected = true
|
|
55
|
-
throw browserTimeout({ timeout })
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
return result
|
|
59
|
-
}
|
|
60
|
-
}
|
package/src/vm.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const pReflect = require('p-reflect')
|
|
4
|
-
const merge = require('deepmerge')
|
|
5
|
-
const { NodeVM } = require('vm2')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
|
|
8
|
-
const DEFAULT_VM_OPTS = {
|
|
9
|
-
console: 'off',
|
|
10
|
-
sandbox: {},
|
|
11
|
-
strict: true,
|
|
12
|
-
require: {
|
|
13
|
-
resolve: pkgName => path.resolve(process.cwd(), 'node_modules', pkgName),
|
|
14
|
-
external: {
|
|
15
|
-
modules: ['serialize-error', 'browserless']
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const createVm = (opts = {}) => new NodeVM(merge(DEFAULT_VM_OPTS, opts))
|
|
21
|
-
|
|
22
|
-
const compile = (vm, fn, scriptPath) => {
|
|
23
|
-
const code = `module.exports = ${fn.toString()}`
|
|
24
|
-
return vm.run(code, scriptPath)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
module.exports = opts => {
|
|
28
|
-
const vm = createVm(opts)
|
|
29
|
-
|
|
30
|
-
return (fn, scriptPath) => {
|
|
31
|
-
const run = compile(vm, fn, scriptPath)
|
|
32
|
-
return (...args) => pReflect(run(...args))
|
|
33
|
-
}
|
|
34
|
-
}
|