@freestyle-sh/with-deno 0.0.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/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # @freestyle-sh/with-deno
2
+
3
+ Deno runtime for [Freestyle](https://freestyle.sh) VMs.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @freestyle-sh/with-deno freestyle-sandboxes
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { freestyle, VmSpec } from "freestyle-sandboxes";
15
+ import { VmDeno } from "@freestyle-sh/with-deno";
16
+
17
+ const spec = new VmSpec({
18
+ with: {
19
+ deno: new VmDeno(),
20
+ },
21
+ });
22
+
23
+ const { vm } = await freestyle.vms.create({ spec });
24
+
25
+ const res = await vm.deno.runCode({
26
+ code: "console.log(JSON.stringify({ hello: 'world', runtime: 'deno' }));",
27
+ });
28
+
29
+ console.log(res);
30
+ // { result: { hello: 'world', runtime: 'deno' }, stdout: '{"hello":"world","runtime":"deno"}\n', statusCode: 0 }
31
+ ```
32
+
33
+ ## Options
34
+
35
+ ```typescript
36
+ new VmDeno({
37
+ version: "2.0.0", // Optional: specific Deno version (default: latest)
38
+ })
39
+ ```
40
+
41
+ | Option | Type | Default | Description |
42
+ |--------|------|---------|-------------|
43
+ | `version` | `string` | `undefined` | Deno version to install. If not specified, installs the latest version. |
44
+
45
+ ## API
46
+
47
+ ### `vm.deno.runCode({ code: string })`
48
+
49
+ Executes JavaScript/TypeScript code using `deno eval`.
50
+
51
+ **Returns:** `Promise<RunCodeResponse>`
52
+
53
+ ```typescript
54
+ type RunCodeResponse<Result> = {
55
+ result: Result; // Parsed JSON from stdout (if valid JSON)
56
+ stdout?: string; // Raw stdout output
57
+ stderr?: string; // Raw stderr output
58
+ statusCode?: number; // Exit code
59
+ };
60
+ ```
61
+
62
+ ### `vm.deno.install(options?)`
63
+
64
+ Installs packages using Deno. Supports both npm packages and JSR (Deno's native registry).
65
+
66
+ ```typescript
67
+ // Install from deno.json in current directory
68
+ await vm.deno.install();
69
+
70
+ // Install from deno.json in specific directory
71
+ await vm.deno.install({ directory: "/app" });
72
+
73
+ // Install npm packages (auto-prefixed with npm:)
74
+ await vm.deno.install({ deps: ["lodash-es", "express"] });
75
+
76
+ // Install JSR packages (use jsr: prefix)
77
+ await vm.deno.install({ deps: ["jsr:@std/path", "jsr:@std/fs"] });
78
+
79
+ // Install with specific versions
80
+ await vm.deno.install({ deps: { "lodash-es": "^4.0.0" } });
81
+
82
+ // Install as dev dependencies
83
+ await vm.deno.install({ deps: ["typescript"], dev: true });
84
+
85
+ // Install globally
86
+ await vm.deno.install({ global: true, deps: ["jsr:@std/cli"] });
87
+ ```
88
+
89
+ **Returns:** `Promise<InstallResult>`
90
+
91
+ ```typescript
92
+ type InstallResult = {
93
+ success: boolean;
94
+ stdout?: string;
95
+ stderr?: string;
96
+ };
97
+ ```
98
+
99
+ ## JSR Packages
100
+
101
+ Deno has native support for [JSR](https://jsr.io) (JavaScript Registry), which hosts TypeScript-first packages including the Deno standard library.
102
+
103
+ ```typescript
104
+ // Install @std/path from JSR
105
+ await vm.deno.install({ deps: ["jsr:@std/path"] });
106
+
107
+ // Use it in code
108
+ const res = await vm.deno.runCode({
109
+ code: `
110
+ import * as path from "jsr:@std/path";
111
+ console.log(JSON.stringify({
112
+ join: path.join("foo", "bar", "baz"),
113
+ basename: path.basename("/home/user/file.txt"),
114
+ }));
115
+ `,
116
+ });
117
+ ```
118
+
119
+ ## Documentation
120
+
121
+ - [Freestyle Documentation](https://docs.freestyle.sh)
122
+ - [Deno Documentation](https://docs.deno.com)
123
+ - [JSR Registry](https://jsr.io)
@@ -0,0 +1,33 @@
1
+ import { VmWith, VmWithInstance, VmSpec } from 'freestyle-sandboxes';
2
+ import { VmJavaScriptRuntimeInstance, JSONValue, RunCodeResponse, InstallOptions, InstallResult, VmJavaScriptRuntime } from '@freestyle-sh/with-type-js';
3
+
4
+ type DenoOptions = {
5
+ version?: string;
6
+ workdir?: string;
7
+ deleteAfterSuccess?: boolean;
8
+ };
9
+ type DenoResolvedOptions = {
10
+ version?: string;
11
+ workdir?: string;
12
+ deleteAfterSuccess: boolean;
13
+ };
14
+ declare class VmDeno extends VmWith<VmDenoInstance> implements VmJavaScriptRuntime<VmJavaScriptRuntimeInstance> {
15
+ options: DenoResolvedOptions;
16
+ constructor(options?: DenoOptions);
17
+ configureSnapshotSpec(spec: VmSpec): VmSpec;
18
+ createInstance(): VmDenoInstance;
19
+ installServiceName(): string;
20
+ }
21
+ declare class VmDenoInstance extends VmWithInstance implements VmJavaScriptRuntimeInstance {
22
+ builder: VmDeno;
23
+ constructor(builder: VmDeno);
24
+ runCode<Result extends JSONValue = any>(args: string | {
25
+ code: string;
26
+ argv?: string[];
27
+ env?: Record<string, string>;
28
+ workdir?: string;
29
+ }): Promise<RunCodeResponse<Result>>;
30
+ install(options?: InstallOptions): Promise<InstallResult>;
31
+ }
32
+
33
+ export { VmDeno };
package/dist/index.js ADDED
@@ -0,0 +1,124 @@
1
+ import { VmWith, VmSpec, VmWithInstance } from 'freestyle-sandboxes';
2
+
3
+ class VmDeno extends VmWith {
4
+ options;
5
+ constructor(options) {
6
+ super();
7
+ this.options = {
8
+ version: options?.version,
9
+ workdir: options?.workdir,
10
+ deleteAfterSuccess: options?.deleteAfterSuccess ?? true
11
+ };
12
+ }
13
+ configureSnapshotSpec(spec) {
14
+ const versionArg = this.options.version ? ` v${this.options.version}` : "";
15
+ const installScript = `#!/bin/bash
16
+ set -e
17
+ export DENO_INSTALL="/opt/deno"
18
+ curl -fsSL https://deno.land/install.sh | sh -s -- --yes${versionArg}
19
+ $DENO_INSTALL/bin/deno --version
20
+ `;
21
+ const denoInit = `export DENO_INSTALL="/opt/deno"
22
+ export PATH="$DENO_INSTALL/bin:$PATH"
23
+ `;
24
+ return this.composeSpecs(
25
+ spec,
26
+ new VmSpec({
27
+ additionalFiles: {
28
+ "/opt/install-deno.sh": {
29
+ content: installScript
30
+ },
31
+ "/etc/profile.d/deno.sh": {
32
+ content: denoInit
33
+ }
34
+ },
35
+ systemd: {
36
+ services: [
37
+ {
38
+ name: "install-deno",
39
+ mode: "oneshot",
40
+ deleteAfterSuccess: this.options.deleteAfterSuccess,
41
+ env: {
42
+ HOME: "/root"
43
+ },
44
+ exec: ["bash /opt/install-deno.sh"],
45
+ timeoutSec: 300
46
+ }
47
+ ]
48
+ }
49
+ })
50
+ );
51
+ }
52
+ createInstance() {
53
+ return new VmDenoInstance(this);
54
+ }
55
+ installServiceName() {
56
+ return "install-deno.service";
57
+ }
58
+ }
59
+ class VmDenoInstance extends VmWithInstance {
60
+ builder;
61
+ constructor(builder) {
62
+ super();
63
+ this.builder = builder;
64
+ }
65
+ async runCode(args) {
66
+ const options = typeof args === "string" ? { code: args } : args;
67
+ const { code, argv, env, workdir } = options;
68
+ const shellEscape = (value) => `'${value.replace(/'/g, "'\\''")}'`;
69
+ const argvArgs = argv?.map(shellEscape).join(" ");
70
+ const envPrefix = env ? `${Object.entries(env).map(([key, value]) => `${key}=${shellEscape(value)}`).join(" ")} ` : "";
71
+ const cdPrefix = workdir ? `cd ${shellEscape(workdir)} && ` : "";
72
+ const command = `${cdPrefix}${envPrefix}/opt/deno/bin/deno eval "${code.replace(/"/g, '\\"')}"${argvArgs ? ` -- ${argvArgs}` : ""}`;
73
+ const result = await this.vm.exec({
74
+ command
75
+ });
76
+ let parsedResult = void 0;
77
+ if (result.stdout) {
78
+ const lines = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
79
+ const lastLine = lines[lines.length - 1];
80
+ if (lastLine) {
81
+ try {
82
+ parsedResult = JSON.parse(lastLine);
83
+ } catch (e) {
84
+ }
85
+ }
86
+ }
87
+ return {
88
+ result: parsedResult,
89
+ stdout: result.stdout ?? void 0,
90
+ stderr: result.stderr ?? void 0,
91
+ statusCode: result.statusCode ?? -1
92
+ };
93
+ }
94
+ async install(options) {
95
+ let command;
96
+ const prefixDep = (dep) => {
97
+ if (dep.startsWith("npm:") || dep.startsWith("jsr:")) {
98
+ return dep;
99
+ }
100
+ return `npm:${dep}`;
101
+ };
102
+ if (options?.global) {
103
+ const deps = options.deps.map(prefixDep);
104
+ command = `/opt/deno/bin/deno install --global --allow-all ${deps.join(" ")}`;
105
+ } else {
106
+ const cdPrefix = options?.directory ? `cd ${options.directory} && ` : "";
107
+ if (!options?.deps) {
108
+ command = `${cdPrefix}/opt/deno/bin/deno install`;
109
+ } else {
110
+ const deps = Array.isArray(options.deps) ? options.deps.map(prefixDep) : Object.entries(options.deps).map(([pkg, ver]) => `${prefixDep(pkg)}@${ver}`);
111
+ const devFlag = options.dev ? " --dev" : "";
112
+ command = `${cdPrefix}/opt/deno/bin/deno add${devFlag} ${deps.join(" ")}`;
113
+ }
114
+ }
115
+ const result = await this.vm.exec({ command });
116
+ return {
117
+ success: result.statusCode === 0,
118
+ stdout: result.stdout ?? void 0,
119
+ stderr: result.stderr ?? void 0
120
+ };
121
+ }
122
+ }
123
+
124
+ export { VmDeno };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@freestyle-sh/with-deno",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "dependencies": {
6
+ "freestyle-sandboxes": "^0.1.14",
7
+ "@freestyle-sh/with-type-js": "0.2.8"
8
+ },
9
+ "devDependencies": {
10
+ "@types/node": "^22.0.0",
11
+ "pkgroll": "^2.11.2",
12
+ "typescript": "^5.8.3"
13
+ },
14
+ "type": "module",
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ }
22
+ },
23
+ "source": "./src/index.ts",
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "scripts": {
28
+ "build": "pkgroll"
29
+ }
30
+ }