@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 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.4",
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
- "@browserless/errors": "^10.5.2",
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
- "p-timeout": "~4.1.0",
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.5.2",
37
+ "@browserless/test": "^10.6.0",
44
38
  "ava": "5",
45
- "browserless": "^10.5.4",
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": "f65b4f579d2ff9408ae9e8363202b8b2f5d14b9b"
57
+ "gitHead": "44f9a1f22ea2020949e8d7465362908e3fd8e1a7"
64
58
  }
package/src/function.js CHANGED
@@ -1,51 +1,22 @@
1
1
  'use strict'
2
2
 
3
- const path = require('path')
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 ({ url, gotoOpts, browserWSEndpoint, ...opts }) => {
11
- const { serializeError } = require('serialize-error')
12
-
13
- const getBrowserless = require('browserless')
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
- const value = await browserFn(url)
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 browserless.destroyContext()
42
- await browserless.browser().then(browser => browser.disconnect())
13
+ await browser.disconnect()
43
14
  }
44
15
  }`
45
16
 
46
- module.exports = ({ url, code, vmOpts, gotoOpts, browserWSEndpoint, ...opts }) => {
47
- const vm = createVm(vmOpts)
48
- const fn = createFn(code)
49
- const run = vm(fn, scriptPath)
50
- return run({ url, gotoOpts, browserWSEndpoint, ...opts })
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 { ensureError, browserTimeout } = require('@browserless/errors')
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
- fn,
17
- { getBrowserless = requireOneOf(['browserless']), retry = 2, timeout = 30000, ...opts } = {}
18
- ) => {
19
- return async (url, fnOpts = {}) => {
20
- const browserlessPromise = getBrowserless()
21
- let isRejected = false
22
-
23
- async function run () {
24
- const browserless = await browserlessPromise
25
- const browser = await browserless.browser()
26
- const browserWSEndpoint = browser.wsEndpoint()
27
-
28
- const { value, reason, isFulfilled } = await runFunction({
29
- url,
30
- code: stringify(fn),
31
- browserWSEndpoint,
32
- ...opts,
33
- ...fnOpts
34
- })
35
-
36
- if (isFulfilled) return value
37
- throw ensureError(reason)
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
- }