Skip to content

Commit 4806ef5

Browse files
breaking change: refactor wasm loading #84
1 parent 56503fa commit 4806ef5

File tree

79 files changed

+1336
-1049
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1336
-1049
lines changed

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,20 @@ This TypeScript package allows you to safely execute **JavaScript AND TypeScript
2121
Here's a simple example of how to use the package:
2222

2323
```typescript
24-
import { type SandboxOptions, loadQuickJs } from '@sebastianwessel/quickjs'
24+
import variant from "@jitl/quickjs-ng-wasmfile-release-sync";
25+
import { type SandboxOptions, loadQuickJs } from "@sebastianwessel/quickjs";
2526

2627
// General setup like loading and init of the QuickJS wasm
2728
// It is a ressource intensive job and should be done only once if possible
28-
const { runSandboxed } = await loadQuickJs()
29+
const { runSandboxed } = await loadQuickJs(variant);
2930

3031
const options: SandboxOptions = {
3132
allowFetch: true, // inject fetch and allow the code to fetch data
3233
allowFs: true, // mount a virtual file system and provide node:fs module
3334
env: {
34-
MY_ENV_VAR: 'env var value',
35+
MY_ENV_VAR: "env var value",
3536
},
36-
}
37+
};
3738

3839
const code = `
3940
import { join } from 'path'
@@ -49,13 +50,16 @@ const fn = async ()=>{
4950
5051
return f.text()
5152
}
52-
53+
5354
export default await fn()
54-
`
55+
`;
5556

56-
const result = await runSandboxed(async ({ evalCode }) => evalCode(code), options)
57+
const result = await runSandboxed(
58+
async ({ evalCode }) => evalCode(code),
59+
options,
60+
);
5761

58-
console.log(result) // { ok: true, data: '<!doctype html>\n<html>\n[....]</html>\n' }
62+
console.log(result); // { ok: true, data: '<!doctype html>\n<html>\n[....]</html>\n' }
5963
```
6064

6165
**[View the full documentation](https://sebastianwessel.github.io/quickjs/)**

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/2.0.5/schema.json",
2+
"$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
33
"assist": { "actions": { "source": { "organizeImports": "on" } } },
44
"files": {
55
"includes": [

bun.lock

Lines changed: 91 additions & 62 deletions
Large diffs are not rendered by default.

example/ai/index.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
import variant from '@jitl/quickjs-ng-wasmfile-release-sync'
12
import OpenAI from 'openai'
23
import { loadQuickJs, type SandboxOptions } from '../../src/index.js'
34

45
// The user instruction
56
const USER_INSTRUCTION = 'I need the content of the title tag from https://purista.dev'
67

78
// Set the LLM - here, Ollama with model Qwen 2.5 7b is used
8-
// biome-ignore lint/complexity/useLiteralKeys: <explanation>
9+
// biome-ignore lint/complexity/useLiteralKeys: ok here
910
const OPENAI_API_KEY = process.env['OPENAI_API_KEY'] ?? ''
10-
// biome-ignore lint/complexity/useLiteralKeys: <explanation>
11-
const OPENAI_API_BASE = process.env['OPENAI_API_BASE'] ?? 'http://localhost:11434/v1'
11+
const OPENAI_API_BASE =
12+
// biome-ignore lint/complexity/useLiteralKeys: ok here
13+
process.env['OPENAI_API_BASE'] ?? 'http://localhost:11434/v1'
1214
const MODEL = 'qwen2.5-coder:7b' //'gpt-4o'
1315

1416
const client = new OpenAI({
@@ -53,7 +55,12 @@ export default await myFunction()
5355
console.log('Generating code')
5456

5557
const chatCompletion = await client.chat.completions.create({
56-
messages: [{ role: 'user', content: promptTemplate.replace('%INSTRUCTION%', USER_INSTRUCTION) }],
58+
messages: [
59+
{
60+
role: 'user',
61+
content: promptTemplate.replace('%INSTRUCTION%', USER_INSTRUCTION),
62+
},
63+
],
5764
model: MODEL,
5865
})
5966

@@ -63,7 +70,7 @@ if (!code) {
6370
throw new Error('Failed to generate code')
6471
}
6572

66-
const { runSandboxed } = await loadQuickJs()
73+
const { runSandboxed } = await loadQuickJs(variant)
6774

6875
const options: SandboxOptions = {
6976
allowFetch: true, // inject fetch and allow the code to fetch data

example/async/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { join, resolve } from 'node:path'
2+
import variant from '@jitl/quickjs-wasmfile-release-asyncify'
23
import type { QuickJSAsyncContext } from 'quickjs-emscripten-core'
34
import { getAsyncModuleLoader, loadAsyncQuickJs, type SandboxAsyncOptions } from '../../src/index.js'
45

5-
const { runSandboxed } = await loadAsyncQuickJs()
6+
const { runSandboxed } = await loadAsyncQuickJs(variant)
67

78
const modulePathNormalizer = async (baseName: string, requestedName: string) => {
89
// import from esm.sh

example/basic/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import variant from '@jitl/quickjs-ng-wasmfile-release-sync'
12
import { loadQuickJs, type SandboxOptions } from '../../src/index.js'
23

34
// General setup like loading and init of the QuickJS wasm
45
// It is a ressource intensive job and should be done only once if possible
5-
const { runSandboxed } = await loadQuickJs()
6+
const { runSandboxed } = await loadQuickJs(variant)
67

78
const options: SandboxOptions = {
89
allowFetch: true, // inject fetch and allow the code to fetch data
@@ -30,7 +31,7 @@ const fn = async ()=>{
3031
const result = await fn()
3132
3233
globalThis.step1 = result
33-
34+
3435
export default result
3536
`
3637

example/browser/playground.html

Lines changed: 138 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en">
3-
<head>
4-
<meta charset="UTF-8">
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>@sebastianwessel/quickjs in browser example</title>
7-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.css">
8-
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
9-
<style>
10-
.CodeMirror {
11-
height: 100%;
12-
}
13-
</style>
14-
</head>
15-
<body class="flex h-screen font-sans">
16-
<div class="flex flex-col w-2/3 border-r border-gray-300">
17-
<div class="bg-gray-100 text-center text-xl font-bold p-4">
18-
Enter your code
19-
</div>
20-
<div class="flex-1">
21-
<textarea id="code" class="h-full w-full">import { readFileSync, writeFileSync } from 'node:fs'
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>@sebastianwessel/quickjs in browser example</title>
7+
<link
8+
rel="stylesheet"
9+
href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.css"
10+
/>
11+
<link
12+
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css"
13+
rel="stylesheet"
14+
/>
15+
<style>
16+
.CodeMirror {
17+
height: 100%;
18+
}
19+
</style>
20+
</head>
21+
<body class="flex h-screen font-sans">
22+
<div class="flex flex-col w-2/3 border-r border-gray-300">
23+
<div class="bg-gray-100 text-center text-xl font-bold p-4">
24+
Enter your code
25+
</div>
26+
<div class="flex-1">
27+
<textarea id="code" class="h-full w-full">
28+
import { readFileSync, writeFileSync } from 'node:fs'
2229
console.info('Starting...')
2330
const fn = async (value)=> {
2431
console.debug(value)
@@ -32,7 +39,7 @@
3239
const response = await fetch('https://sebastianwessel.github.io/quickjs/example-request.html')
3340
if(!response.ok) {
3441
console.error('Request failed: ' + response.status + ' ' + response.statusText)
35-
return
42+
return
3643
}
3744
const body = await response.text()
3845
console.log('Response body: '+ body)
@@ -43,99 +50,123 @@
4350

4451
console.warn('top-level await is nice')
4552

46-
export default await fn('Hello World')</textarea>
47-
</div>
48-
<button id="runButton" class="bg-blue-500 text-white font-semibold text-lg p-4 hover:bg-blue-700">
49-
Run Code
50-
</button>
51-
</div>
52-
<div class="flex flex-col w-1/3">
53-
<div class="bg-gray-100 p-4">
54-
<div class="text-center text-xl font-bold ">Result</div>
55-
<pre id="output" class="font-semibold"></pre>
53+
export default await fn('Hello World')</textarea
54+
>
55+
</div>
56+
<button
57+
id="runButton"
58+
class="bg-blue-500 text-white font-semibold text-lg p-4 hover:bg-blue-700"
59+
>
60+
Run Code
61+
</button>
5662
</div>
57-
<div id="consoleOutput" class="flex-1 p-4 bg-black text-white overflow-y-auto">
58-
<pre id="console"></pre>
63+
<div class="flex flex-col w-1/3">
64+
<div class="bg-gray-100 p-4">
65+
<div class="text-center text-xl font-bold">Result</div>
66+
<pre id="output" class="font-semibold"></pre>
67+
</div>
68+
<div
69+
id="consoleOutput"
70+
class="flex-1 p-4 bg-black text-white overflow-y-auto"
71+
>
72+
<pre id="console"></pre>
73+
</div>
5974
</div>
60-
</div>
6175

62-
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.js"></script>
63-
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/javascript/javascript.min.js"></script>
64-
<script type="module">
65-
var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
66-
mode: { name: "javascript", typescript: true },
67-
lineNumbers: true
68-
});
76+
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.js"></script>
77+
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/javascript/javascript.min.js"></script>
78+
<script type="module">
79+
var editor = CodeMirror.fromTextArea(
80+
document.getElementById("code"),
81+
{
82+
mode: { name: "javascript", typescript: true },
83+
lineNumbers: true,
84+
},
85+
);
6986

70-
function logMessage(type, message) {
71-
var outputElement = document.getElementById('console');
72-
var logMessage = document.createElement('div');
73-
logMessage.className = type;
74-
logMessage.textContent = message;
75-
outputElement.appendChild(logMessage);
76-
}
77-
78-
// =========== QuickJS =====================
79-
import { loadQuickJs } from "https://esm.sh/@sebastianwessel/[email protected]"
80-
import "https://esm.sh/typescript"
87+
function logMessage(type, message) {
88+
var outputElement = document.getElementById("console");
89+
var logMessage = document.createElement("div");
90+
logMessage.className = type;
91+
logMessage.textContent = message;
92+
outputElement.appendChild(logMessage);
93+
}
8194

82-
const { runSandboxed } = await loadQuickJs('https://esm.sh/@jitl/quickjs-wasmfile-release-sync')
95+
// =========== QuickJS =====================
96+
import { loadQuickJs } from "https://esm.sh/@sebastianwessel/[email protected]";
97+
import "https://esm.sh/typescript";
98+
import variant from "https://esm.sh/@jitl/quickjs-wasmfile-release-sync";
8399

84-
const fetchAdapter = async (url,param)=> {
85-
const res = await fetch(url,{...param})
86-
console.log(res)
87-
return {
88-
status: res.status,
89-
ok: res.ok,
90-
statusText: res.statusText,
91-
json: async () => await res.json(),
92-
text: async () => await res.text(),
93-
formData: () => res.formData(),
94-
headers: res.headers,
95-
type: res.type,
96-
url: res.url,
97-
blob: async () => await res.blob(),
98-
bodyUsed: res.bodyUsed,
99-
redirected: res.redirected,
100-
body: res.body,
101-
arrayBuffer: async () => await res.arrayBuffer(),
102-
clone: () => res.clone(),
103-
}
104-
}
100+
const { runSandboxed } = await loadQuickJs(variant);
105101

106-
document.getElementById('runButton').addEventListener('click', async function() {
107-
var code = editor.getValue();
108-
var outputElement = document.getElementById('output');
109-
var consoleElement = document.getElementById('console');
110-
outputElement.innerHTML=''
111-
consoleElement.innerHTML=''
102+
const fetchAdapter = async (url, param) => {
103+
const res = await fetch(url, { ...param });
104+
console.log(res);
105+
return {
106+
status: res.status,
107+
ok: res.ok,
108+
statusText: res.statusText,
109+
json: async () => await res.json(),
110+
text: async () => await res.text(),
111+
formData: () => res.formData(),
112+
headers: res.headers,
113+
type: res.type,
114+
url: res.url,
115+
blob: async () => await res.blob(),
116+
bodyUsed: res.bodyUsed,
117+
redirected: res.redirected,
118+
body: res.body,
119+
arrayBuffer: async () => await res.arrayBuffer(),
120+
clone: () => res.clone(),
121+
};
122+
};
112123

113-
const options = {
114-
allowFetch: true, // inject fetch and allow the code to fetch data
115-
allowFs: true, // mount a virtual file system and provide node:fs module
116-
fetchAdapter, // browser-fetch
117-
env: {
118-
MY_ENV_VAR: 'env var value',
119-
},
120-
console: {
121-
log: (message)=>logMessage('log text-gray-400', message),
122-
info: (message)=>logMessage('info text-blue-500', message),
123-
warn: (message)=>logMessage('warn text-yellow-500', message),
124-
error: (message)=>logMessage('error text-red-500', message),
125-
}
126-
}
127-
128-
try {
129-
const res = await runSandboxed(async ({ evalCode }) => {
130-
return evalCode(code)
131-
},options)
124+
document
125+
.getElementById("runButton")
126+
.addEventListener("click", async function () {
127+
var code = editor.getValue();
128+
var outputElement = document.getElementById("output");
129+
var consoleElement = document.getElementById("console");
130+
outputElement.innerHTML = "";
131+
consoleElement.innerHTML = "";
132132

133-
outputElement.innerHTML = JSON.stringify(res,null,2).replaceAll('\\n','<br>')
134-
} catch (e) {
135-
console.error(e);
136-
logMessage('error text-red-500', JSON.stringify(e).replaceAll('\\n','<br>'))
137-
}
138-
});
139-
</script>
140-
</body>
133+
const options = {
134+
allowFetch: true, // inject fetch and allow the code to fetch data
135+
allowFs: true, // mount a virtual file system and provide node:fs module
136+
fetchAdapter, // browser-fetch
137+
env: {
138+
MY_ENV_VAR: "env var value",
139+
},
140+
console: {
141+
log: (message) =>
142+
logMessage("log text-gray-400", message),
143+
info: (message) =>
144+
logMessage("info text-blue-500", message),
145+
warn: (message) =>
146+
logMessage("warn text-yellow-500", message),
147+
error: (message) =>
148+
logMessage("error text-red-500", message),
149+
},
150+
};
151+
152+
try {
153+
const res = await runSandboxed(async ({ evalCode }) => {
154+
return evalCode(code);
155+
}, options);
156+
157+
outputElement.innerHTML = JSON.stringify(
158+
res,
159+
null,
160+
2,
161+
).replaceAll("\\n", "<br>");
162+
} catch (e) {
163+
console.error(e);
164+
logMessage(
165+
"error text-red-500",
166+
JSON.stringify(e).replaceAll("\\n", "<br>"),
167+
);
168+
}
169+
});
170+
</script>
171+
</body>
141172
</html>

0 commit comments

Comments
 (0)