@freestyle-sh/with-nodejs 0.2.0 → 0.2.2
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 +98 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.js +45 -15
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# @freestyle-sh/with-nodejs
|
|
2
|
+
|
|
3
|
+
Node.js runtime via [NVM](https://github.com/nvm-sh/nvm) for [Freestyle](https://freestyle.sh) VMs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @freestyle-sh/with-nodejs freestyle-sandboxes
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { freestyle } from "freestyle-sandboxes";
|
|
15
|
+
import { VmNodeJs } from "@freestyle-sh/with-nodejs";
|
|
16
|
+
|
|
17
|
+
const { vm } = await freestyle.vms.create({
|
|
18
|
+
with: {
|
|
19
|
+
node: new VmNodeJs(),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const res = await vm.node.runCode({
|
|
24
|
+
code: "console.log(JSON.stringify({ hello: 'world' }));"
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
console.log(res);
|
|
28
|
+
// { result: { hello: 'world' }, stdout: '{"hello":"world"}\n', statusCode: 0 }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
new VmNodeJs({
|
|
35
|
+
version: "22", // Optional: Node.js version (default: "24")
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
| Option | Type | Default | Description |
|
|
40
|
+
|--------|------|---------|-------------|
|
|
41
|
+
| `version` | `string` | `"24"` | Node.js version to install via NVM. |
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
|
|
45
|
+
### `vm.node.runCode({ code: string })`
|
|
46
|
+
|
|
47
|
+
Executes JavaScript code in the Node.js runtime.
|
|
48
|
+
|
|
49
|
+
**Returns:** `Promise<RunCodeResponse>`
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
type RunCodeResponse<Result> = {
|
|
53
|
+
result: Result; // Parsed JSON from stdout (if valid JSON)
|
|
54
|
+
stdout?: string; // Raw stdout output
|
|
55
|
+
stderr?: string; // Raw stderr output
|
|
56
|
+
statusCode?: number; // Exit code
|
|
57
|
+
};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `vm.node.install(options?)`
|
|
61
|
+
|
|
62
|
+
Installs npm packages.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Install from package.json in current directory
|
|
66
|
+
await vm.node.install();
|
|
67
|
+
|
|
68
|
+
// Install from package.json in specific directory
|
|
69
|
+
await vm.node.install({ directory: "/app" });
|
|
70
|
+
|
|
71
|
+
// Install specific packages
|
|
72
|
+
await vm.node.install({ deps: ["lodash", "express"] });
|
|
73
|
+
|
|
74
|
+
// Install with specific versions
|
|
75
|
+
await vm.node.install({ deps: { "lodash": "^4.0.0", "express": "~5.0.0" } });
|
|
76
|
+
|
|
77
|
+
// Install as dev dependencies
|
|
78
|
+
await vm.node.install({ deps: ["typescript"], dev: true });
|
|
79
|
+
|
|
80
|
+
// Install globally
|
|
81
|
+
await vm.node.install({ global: true, deps: ["typescript"] });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Returns:** `Promise<InstallResult>`
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
type InstallResult = {
|
|
88
|
+
success: boolean;
|
|
89
|
+
stdout?: string;
|
|
90
|
+
stderr?: string;
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Documentation
|
|
95
|
+
|
|
96
|
+
- [Freestyle Documentation](https://docs.freestyle.sh)
|
|
97
|
+
- [Node.js Documentation](https://nodejs.org/docs)
|
|
98
|
+
- [NVM Documentation](https://github.com/nvm-sh/nvm)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { VmWith, VmWithInstance,
|
|
2
|
-
import { VmJavaScriptRuntimeInstance, JSONValue, RunCodeResponse, VmJavaScriptRuntime } from '@freestyle-sh/with-type-js';
|
|
1
|
+
import { VmWith, VmWithInstance, VmSpec } from 'freestyle-sandboxes';
|
|
2
|
+
import { VmJavaScriptRuntimeInstance, JSONValue, RunCodeResponse, InstallOptions, InstallResult, VmJavaScriptRuntime } from '@freestyle-sh/with-type-js';
|
|
3
3
|
|
|
4
4
|
type NodeJsOptions = {
|
|
5
5
|
version?: string;
|
|
@@ -12,16 +12,20 @@ type NodeJsResolvedOptions = {
|
|
|
12
12
|
declare class VmNodeJs extends VmWith<NodeJsRuntimeInstance> implements VmJavaScriptRuntime<VmJavaScriptRuntimeInstance> {
|
|
13
13
|
options: NodeJsResolvedOptions;
|
|
14
14
|
constructor(options?: NodeJsOptions);
|
|
15
|
-
|
|
15
|
+
configureSnapshotSpec(spec: VmSpec): VmSpec;
|
|
16
16
|
createInstance(): NodeJsRuntimeInstance;
|
|
17
17
|
installServiceName(): string;
|
|
18
18
|
}
|
|
19
19
|
declare class NodeJsRuntimeInstance extends VmWithInstance implements VmJavaScriptRuntimeInstance {
|
|
20
20
|
builder: VmNodeJs;
|
|
21
21
|
constructor(builder: VmNodeJs);
|
|
22
|
-
runCode<Result extends JSONValue = any>(
|
|
22
|
+
runCode<Result extends JSONValue = any>(args: string | {
|
|
23
23
|
code: string;
|
|
24
|
+
argv?: string[];
|
|
25
|
+
env?: Record<string, string>;
|
|
26
|
+
workdir?: string;
|
|
24
27
|
}): Promise<RunCodeResponse<Result>>;
|
|
28
|
+
install(options?: InstallOptions): Promise<InstallResult>;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
export { VmNodeJs };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VmWith,
|
|
1
|
+
import { VmWith, VmSpec, VmWithInstance } from 'freestyle-sandboxes';
|
|
2
2
|
|
|
3
3
|
class VmNodeJs extends VmWith {
|
|
4
4
|
options;
|
|
@@ -9,7 +9,7 @@ class VmNodeJs extends VmWith {
|
|
|
9
9
|
workdir: options?.workdir
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
configureSnapshotSpec(spec) {
|
|
13
13
|
const installScript = `#!/bin/bash
|
|
14
14
|
set -e
|
|
15
15
|
export NVM_DIR="/opt/nvm"
|
|
@@ -25,8 +25,9 @@ npm -v
|
|
|
25
25
|
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
|
26
26
|
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
|
|
27
27
|
`;
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
return this.composeSpecs(
|
|
29
|
+
spec,
|
|
30
|
+
new VmSpec({
|
|
30
31
|
additionalFiles: {
|
|
31
32
|
"/opt/install-nodejs.sh": {
|
|
32
33
|
content: installScript
|
|
@@ -47,8 +48,7 @@ npm -v
|
|
|
47
48
|
]
|
|
48
49
|
}
|
|
49
50
|
})
|
|
50
|
-
|
|
51
|
-
return this.compose(existingConfig, nodeJsConfig);
|
|
51
|
+
);
|
|
52
52
|
}
|
|
53
53
|
createInstance() {
|
|
54
54
|
return new NodeJsRuntimeInstance(this);
|
|
@@ -63,19 +63,28 @@ class NodeJsRuntimeInstance extends VmWithInstance {
|
|
|
63
63
|
super();
|
|
64
64
|
this.builder = builder;
|
|
65
65
|
}
|
|
66
|
-
async runCode({
|
|
67
|
-
code
|
|
68
|
-
|
|
66
|
+
async runCode(args) {
|
|
67
|
+
const options = typeof args === "string" ? { code: args } : args;
|
|
68
|
+
const { code, argv, env, workdir } = options;
|
|
69
|
+
const shellEscape = (value) => `'${value.replace(/'/g, "'\\''")}'`;
|
|
70
|
+
const argvArgs = argv?.map(shellEscape).join(" ");
|
|
71
|
+
const envPrefix = env ? `${Object.entries(env).map(([key, value]) => `${key}=${shellEscape(value)}`).join(" ")} ` : "";
|
|
72
|
+
const cdPrefix = workdir ? `cd ${shellEscape(workdir)} && ` : "";
|
|
73
|
+
const command = `${cdPrefix}${envPrefix}node -e "${code.replace(/"/g, '\\"')}"${argvArgs ? ` -- ${argvArgs}` : ""}`;
|
|
69
74
|
const result = await this.vm.exec({
|
|
70
|
-
command
|
|
75
|
+
command
|
|
71
76
|
});
|
|
72
77
|
let parsedResult = void 0;
|
|
73
78
|
if (result.stdout) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
const lines = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
80
|
+
const lastLine = lines[lines.length - 1];
|
|
81
|
+
if (lastLine) {
|
|
82
|
+
try {
|
|
83
|
+
parsedResult = JSON.parse(lastLine);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (result.stderr) {
|
|
86
|
+
`Failed to parse JSON output. Stderr: ${result.stderr}`;
|
|
87
|
+
}
|
|
79
88
|
}
|
|
80
89
|
}
|
|
81
90
|
}
|
|
@@ -86,6 +95,27 @@ class NodeJsRuntimeInstance extends VmWithInstance {
|
|
|
86
95
|
statusCode: result.statusCode ?? -1
|
|
87
96
|
};
|
|
88
97
|
}
|
|
98
|
+
async install(options) {
|
|
99
|
+
let command;
|
|
100
|
+
if (options?.global) {
|
|
101
|
+
command = `npm install -g ${options.deps.join(" ")}`;
|
|
102
|
+
} else {
|
|
103
|
+
const cdPrefix = options?.directory ? `cd ${options.directory} && ` : "";
|
|
104
|
+
if (!options?.deps) {
|
|
105
|
+
command = `${cdPrefix}npm install`;
|
|
106
|
+
} else {
|
|
107
|
+
const deps = Array.isArray(options.deps) ? options.deps : Object.entries(options.deps).map(([pkg, ver]) => `${pkg}@${ver}`);
|
|
108
|
+
const devFlag = options.dev ? " --save-dev" : "";
|
|
109
|
+
command = `${cdPrefix}npm install${devFlag} ${deps.join(" ")}`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const result = await this.vm.exec({ command });
|
|
113
|
+
return {
|
|
114
|
+
success: result.statusCode === 0,
|
|
115
|
+
stdout: result.stdout ?? void 0,
|
|
116
|
+
stderr: result.stderr ?? void 0
|
|
117
|
+
};
|
|
118
|
+
}
|
|
89
119
|
}
|
|
90
120
|
|
|
91
121
|
export { VmNodeJs };
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freestyle-sh/with-nodejs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"freestyle-sandboxes": "^0.1.
|
|
7
|
-
"@freestyle-sh/with-type-js": "^0.2.
|
|
6
|
+
"freestyle-sandboxes": "^0.1.8",
|
|
7
|
+
"@freestyle-sh/with-type-js": "^0.2.2"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"main": "./dist/index.js",
|