Skip to main content

Example on GitHub

Runnable example for sandboxed TypeScript compilation.
The @secure-exec/typescript companion package runs the TypeScript compiler inside a sandbox for safe type checking and compilation of untrusted code.

Runnable example

import {
  NodeRuntime,
  allowAllFs,
  createNodeDriver,
  createNodeRuntimeDriverFactory,
} from "../../../packages/secure-exec/src/index.ts";
import { createTypeScriptTools } from "../../../packages/secure-exec-typescript/src/index.ts";

const sourceText = `
  export const message: string = "hello from typescript";
`;

const systemDriver = createNodeDriver();
const runtimeDriverFactory = createNodeRuntimeDriverFactory();
const compilerSystemDriver = createNodeDriver({
  moduleAccess: {
    cwd: process.cwd(),
  },
  permissions: { ...allowAllFs },
});

const runtime = new NodeRuntime({
  systemDriver,
  runtimeDriverFactory,
});

const ts = createTypeScriptTools({
  systemDriver: compilerSystemDriver,
  runtimeDriverFactory,
  compilerSpecifier: "/root/node_modules/typescript/lib/typescript.js",
});

try {
  const typecheck = await ts.typecheckSource({
    sourceText,
    filePath: "/root/example.ts",
    compilerOptions: {
      module: "commonjs",
      target: "es2022",
    },
  });

  if (!typecheck.success) {
    throw new Error(typecheck.diagnostics.map((diagnostic) => diagnostic.message).join("\n"));
  }

  const compiled = await ts.compileSource({
    sourceText,
    filePath: "/root/example.ts",
    compilerOptions: {
      module: "commonjs",
      target: "es2022",
    },
  });

  if (!compiled.success || !compiled.outputText) {
    throw new Error(compiled.diagnostics.map((diagnostic) => diagnostic.message).join("\n"));
  }

  const result = await runtime.run<{ message: string }>(compiled.outputText, "/root/example.js");
  const message = result.exports?.message;

  if (result.code !== 0 || message !== "hello from typescript") {
    throw new Error(`Unexpected runtime result: ${JSON.stringify(result)}`);
  }

  console.log(
    JSON.stringify({
      ok: true,
      message,
      summary: "sandbox typechecked, compiled, and ran a TypeScript snippet",
    }),
  );
} finally {
  runtime.dispose();
}
Source: examples/features/src/typescript.ts

Install

pnpm add @secure-exec/typescript

Setup

import { createNodeDriver, createNodeRuntimeDriverFactory } from "secure-exec";
import { createTypeScriptTools } from "@secure-exec/typescript";

const ts = createTypeScriptTools({
  systemDriver: createNodeDriver(),
  runtimeDriverFactory: createNodeRuntimeDriverFactory(),
});
Options:
OptionTypeDefaultDescription
systemDriverSystemDriverrequiredCompiler runtime capabilities and filesystem view
runtimeDriverFactoryNodeRuntimeDriverFactoryrequiredCreates the compiler sandbox
memoryLimitnumber512Compiler isolate memory cap in MB
cpuTimeLimitMsnumberCompiler CPU time budget in ms
compilerSpecifierstring"typescript"Module specifier for the TypeScript compiler

Type-check a source string

const result = await ts.typecheckSource({
  sourceText: "const x: number = 'hello';",
});

console.log(result.success); // false
console.log(result.diagnostics[0].message);
// "Type 'string' is not assignable to type 'number'."

Type-check a project

const result = await ts.typecheckProject({
  cwd: "/app",
  configFilePath: "/app/tsconfig.json",
});

for (const d of result.diagnostics) {
  console.log(`${d.filePath}:${d.line} ${d.message}`);
}

Compile a source string

const result = await ts.compileSource({
  sourceText: "const x: number = 42; export default x;",
});

console.log(result.outputText);
// "const x = 42; export default x;"

Compile a project

const result = await ts.compileProject({
  cwd: "/app",
});

console.log(result.emittedFiles); // ["/app/dist/index.js", ...]
console.log(result.success); // true

Diagnostic shape

type TypeScriptDiagnostic = {
  code: number;
  category: "error" | "warning" | "suggestion" | "message";
  message: string;
  filePath?: string;
  line?: number;
  column?: number;
};