isolated-function 0.1.14 → 0.1.15
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/README.md +27 -2
- package/package.json +2 -1
- package/src/compile/index.js +28 -23
- package/src/debug.js +19 -0
- package/src/index.js +14 -13
package/README.md
CHANGED
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
- [=\> (fn(\[...args\]), teardown())](#-fnargs-teardown)
|
|
36
36
|
- [fn](#fn)
|
|
37
37
|
- [teardown](#teardown)
|
|
38
|
+
- [Environment Variables](#environment-variables)
|
|
39
|
+
- [`ISOLATED_FUNCTIONS_MINIFY`](#isolated_functions_minify)
|
|
40
|
+
- [`DEBUG`](#debug)
|
|
38
41
|
- [License](#license)
|
|
39
42
|
|
|
40
43
|
## Install
|
|
@@ -262,9 +265,19 @@ Timeout after a specified amount of time, in milliseconds.
|
|
|
262
265
|
##### tmpdir
|
|
263
266
|
|
|
264
267
|
Type: `function`<br>
|
|
265
|
-
Default: `fs.mkdtemp(path.join(require('os').tmpdir(), 'compile-'))`
|
|
266
268
|
|
|
267
|
-
|
|
269
|
+
It setup the temporal folder to be used for installing code dependencies.
|
|
270
|
+
|
|
271
|
+
The default implementation is:
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
const tmpdir = async () => {
|
|
275
|
+
const cwd = await fs.mkdtemp(path.join(require('os').tmpdir(), 'compile-'))
|
|
276
|
+
await fs.mkdir(cwd, { recursive: true })
|
|
277
|
+
const cleanup = () => fs.rm(cwd, { recursive: true, force: true })
|
|
278
|
+
return { cwd, cleanup }
|
|
279
|
+
}
|
|
280
|
+
```
|
|
268
281
|
|
|
269
282
|
### => (fn([...args]), teardown())
|
|
270
283
|
|
|
@@ -280,6 +293,18 @@ Type: `function`
|
|
|
280
293
|
|
|
281
294
|
A function to be called to release resources associated with the **isolated-function**.
|
|
282
295
|
|
|
296
|
+
## Environment Variables
|
|
297
|
+
|
|
298
|
+
#### `ISOLATED_FUNCTIONS_MINIFY`
|
|
299
|
+
|
|
300
|
+
Default: `true`
|
|
301
|
+
|
|
302
|
+
When is `false`, it disabled minify the compiled code.
|
|
303
|
+
|
|
304
|
+
#### `DEBUG`
|
|
305
|
+
|
|
306
|
+
Pass `DEBUG=isolated-function` for enabling debug timing output.
|
|
307
|
+
|
|
283
308
|
## License
|
|
284
309
|
|
|
285
310
|
**isolated-function** © [Kiko Beats](https://kikobeats.com), released under the [MIT](https://github.com/Kikobeats/isolated-function/blob/master/LICENSE.md) License.<br>
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "isolated-function",
|
|
3
3
|
"description": "Runs untrusted code in a Node.js v8 sandbox.",
|
|
4
4
|
"homepage": "https://github.com/Kikobeats/isolated-function",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.15",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.js"
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@kikobeats/time-span": "~1.0.5",
|
|
37
37
|
"acorn": "~8.12.1",
|
|
38
38
|
"acorn-walk": "~8.3.3",
|
|
39
|
+
"debug-logfmt": "~1.2.3",
|
|
39
40
|
"ensure-error": "~3.0.1",
|
|
40
41
|
"esbuild": "~0.23.1",
|
|
41
42
|
"serialize-error": "8",
|
package/src/compile/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const path = require('path')
|
|
|
9
9
|
const transformDependencies = require('./transform-dependencies')
|
|
10
10
|
const detectDependencies = require('./detect-dependencies')
|
|
11
11
|
const generateTemplate = require('../template')
|
|
12
|
+
const { duration } = require('../debug')
|
|
12
13
|
|
|
13
14
|
const MINIFY = (() => {
|
|
14
15
|
return process.env.ISOLATED_FUNCTIONS_MINIFY !== 'false'
|
|
@@ -29,39 +30,43 @@ const packageManager = (() => {
|
|
|
29
30
|
}
|
|
30
31
|
})()
|
|
31
32
|
|
|
32
|
-
const tmpdirDefault = () =>
|
|
33
|
-
|
|
34
|
-
const getTmp = async (content, tmpdir) => {
|
|
35
|
-
const cwd = await tmpdir()
|
|
33
|
+
const tmpdirDefault = async () => {
|
|
34
|
+
const cwd = await fs.mkdtemp(path.join(require('os').tmpdir(), 'compile-'))
|
|
36
35
|
await fs.mkdir(cwd, { recursive: true })
|
|
37
|
-
|
|
38
|
-
const filepath = path.join(cwd, 'index.js')
|
|
39
|
-
await fs.writeFile(filepath, content)
|
|
40
|
-
|
|
41
36
|
const cleanup = () => fs.rm(cwd, { recursive: true, force: true })
|
|
42
|
-
return {
|
|
37
|
+
return { cwd, cleanup }
|
|
43
38
|
}
|
|
44
39
|
|
|
45
40
|
module.exports = async (snippet, tmpdir = tmpdirDefault) => {
|
|
46
41
|
const compiledTemplate = generateTemplate(snippet)
|
|
47
42
|
const dependencies = detectDependencies(compiledTemplate)
|
|
48
|
-
const tmp = await getTmp(transformDependencies(compiledTemplate), tmpdir)
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
await
|
|
52
|
-
cwd: tmp.cwd
|
|
53
|
-
})
|
|
44
|
+
const content = transformDependencies(compiledTemplate)
|
|
45
|
+
const tmpDir = await duration('tmpdir', tmpdir)
|
|
54
46
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
write: false,
|
|
60
|
-
platform: 'node'
|
|
61
|
-
})
|
|
47
|
+
await duration('npm:init', () => $(packageManager.init, { cwd: tmpDir.cwd }))
|
|
48
|
+
await duration('npm:install', () =>
|
|
49
|
+
$(`${packageManager.install} ${dependencies.join(' ')}`, { cwd: tmpDir.cwd })
|
|
50
|
+
)
|
|
62
51
|
|
|
63
|
-
await
|
|
64
|
-
|
|
52
|
+
const result = await duration('esbuild', () =>
|
|
53
|
+
esbuild.build({
|
|
54
|
+
stdin: {
|
|
55
|
+
contents: content,
|
|
56
|
+
resolveDir: tmpDir.cwd,
|
|
57
|
+
sourcefile: 'index.js'
|
|
58
|
+
},
|
|
59
|
+
bundle: true,
|
|
60
|
+
...MINIFY,
|
|
61
|
+
write: false,
|
|
62
|
+
platform: 'node'
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
content: result.outputFiles[0].text,
|
|
68
|
+
cleanupPromise: duration('tmpDir:cleanup', tmpDir.cleanup)
|
|
69
|
+
}
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
module.exports.detectDependencies = detectDependencies
|
package/src/debug.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const debug = require('debug-logfmt')('isolated-function')
|
|
4
|
+
|
|
5
|
+
const duration = async (name, fn) => {
|
|
6
|
+
const duration = debug.duration(name)
|
|
7
|
+
|
|
8
|
+
return Promise.resolve(fn())
|
|
9
|
+
.then(result => {
|
|
10
|
+
duration()
|
|
11
|
+
return result
|
|
12
|
+
})
|
|
13
|
+
.catch(error => {
|
|
14
|
+
duration.error()
|
|
15
|
+
throw error
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { debug, duration }
|
package/src/index.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const { deserializeError } = require('serialize-error')
|
|
4
4
|
const timeSpan = require('@kikobeats/time-span')()
|
|
5
|
+
const { Readable } = require('node:stream')
|
|
5
6
|
const $ = require('tinyspawn')
|
|
6
|
-
const path = require('path')
|
|
7
7
|
|
|
8
8
|
const compile = require('./compile')
|
|
9
|
+
const { debug } = require('./debug')
|
|
9
10
|
|
|
10
11
|
const createError = ({ name, message, ...props }) => {
|
|
11
12
|
const error = new Error(message)
|
|
@@ -14,12 +15,8 @@ const createError = ({ name, message, ...props }) => {
|
|
|
14
15
|
return error
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const flags = ({
|
|
18
|
-
const flags = [
|
|
19
|
-
'--disable-warning=ExperimentalWarning',
|
|
20
|
-
'--experimental-permission',
|
|
21
|
-
`--allow-fs-read=${filename}`
|
|
22
|
-
]
|
|
18
|
+
const flags = ({ memory }) => {
|
|
19
|
+
const flags = ['--disable-warning=ExperimentalWarning', '--experimental-permission']
|
|
23
20
|
if (memory) flags.push(`--max-old-space-size=${memory}`)
|
|
24
21
|
return flags.join(' ')
|
|
25
22
|
}
|
|
@@ -31,22 +28,26 @@ module.exports = (snippet, { tmpdir, timeout, memory, throwError = true } = {})
|
|
|
31
28
|
const fn = async (...args) => {
|
|
32
29
|
let duration
|
|
33
30
|
try {
|
|
34
|
-
const {
|
|
31
|
+
const { content, cleanupPromise } = await compilePromise
|
|
35
32
|
|
|
36
|
-
const cwd = path.dirname(filepath)
|
|
37
|
-
const filename = path.basename(filepath)
|
|
38
33
|
duration = timeSpan()
|
|
39
|
-
const
|
|
40
|
-
cwd,
|
|
34
|
+
const subprocess = $('node', ['-', JSON.stringify(args)], {
|
|
41
35
|
env: {
|
|
42
36
|
...process.env,
|
|
43
|
-
NODE_OPTIONS: flags({
|
|
37
|
+
NODE_OPTIONS: flags({ memory })
|
|
44
38
|
},
|
|
45
39
|
timeout,
|
|
46
40
|
killSignal: 'SIGKILL'
|
|
47
41
|
})
|
|
42
|
+
Readable.from(content).pipe(subprocess.stdin)
|
|
43
|
+
const [{ stdout }] = await Promise.all([subprocess, cleanupPromise])
|
|
48
44
|
const { isFulfilled, value, profiling, logging } = JSON.parse(stdout)
|
|
49
45
|
profiling.duration = duration()
|
|
46
|
+
debug('node', {
|
|
47
|
+
duration: `${Math.round(profiling.duration / 100)}s`,
|
|
48
|
+
memory: `${Math.round(profiling.memory / (1024 * 1024))}MiB`
|
|
49
|
+
})
|
|
50
|
+
|
|
50
51
|
return isFulfilled
|
|
51
52
|
? { isFulfilled, value, profiling, logging }
|
|
52
53
|
: throwError
|