QuickJS sandbox

A basic example on how to run user provided code in a sandbox in the browser with svelte

Table of Contents

What is this?

QuickJS allows you to run JavaScript code in a sandbox in the browser (or on the server). It also allows you to define custom environment variables for the sandbox, limit memory, modify fetch requests, and much more.

Install packages

pnpm i @sebastianwessel/quickjs vite-plugin-node-polyfills

Change vite config

Modify vite config to include node polyfills vite.config.ts

import { nodePolyfills } from 'vite-plugin-node-polyfills';

export default defineConfig({
	plugins: [
		sveltekit(),
		nodePolyfills({
			include: ['buffer', 'path', 'stream', 'util', 'fs'],
			overrides: {
				fs: 'memfs'
			},
			protocolImports: true
		}) // Used for @sebastianwessel/quickjs
	]
});

Svelte file

<script lang="ts">
	import { onMount } from 'svelte';
	let value = $state(`//Run code
let name = env.getName();
export default name
`);
	let result: OkResponse | ErrorResponse | undefined = $state();
	import { loadQuickJs, type ErrorResponse, type OkResponse } from '@sebastianwessel/quickjs';

	type RunSandboxedType = Awaited<ReturnType<typeof loadQuickJs>>['runSandboxed'];
	let run: RunSandboxedType;

	type Options = Parameters<Awaited<ReturnType<typeof loadQuickJs>>['runSandboxed']>[1];
	const options: Options = {
		env: {
			getName() {
				return 'SvelteKit';
			}
		}
	};

	async function execute(code: string) {
		const returnValue = await run(async ({ evalCode }) => {
			return evalCode(code);
		}, options);
		return returnValue;
	}

	onMount(async () => {
		const { runSandboxed } = await loadQuickJs(
			'https://esm.sh/@jitl/quickjs-wasmfile-release-sync'
		);

		run = runSandboxed;
	});
</script>

<textarea rows="4" style="width: 100%" bind:value></textarea>
<br />
<button
	onclick={async () => {
		result = await execute(value);
	}}>Execute</button
>

<pre>{JSON.stringify(result, null, 2)}</pre>