October 23, 2026 · In Ayotl
A course that runs your code without letting it out
The Learn section has lessons with code you can edit and run. Running it isn't the problem: the problem is that a student's infinite loop or fetch mustn't take the page down. The answer was a fresh Worker per run, with its own security policy and three seconds to live.
Every data structures lesson ships a JavaScript program: explanation on top, the code in an editor and, beside it, what it prints. The student changes it and hits RUN. Doing that with eval in the page is the first idea and the worst one: a while (true) freezes the whole tab, and the code has the DOM, the cookies and the network at hand.
So the code runs in a Worker created for that run and thrown away afterwards. If it hasn't answered within three seconds, it gets terminate() and the page says it took too long: the infinite loop dies with its thread, not with the tab. Whatever console.log prints comes back through postMessage, already formatted the way Node would.
// next.config.ts: el script del Worker lleva su propia política.
// Puede evaluar código (para eso está) y nada más: ni red, ni scripts de fuera.
{ source: "/aprende/ejecutor.js",
headers: [{ key: "Content-Security-Policy",
value: "default-src 'none'; script-src 'self' 'unsafe-eval'" }] }There was a silly stumble: the proxy that maps routes to English (/en/learn ↔ /aprende) was also intercepting the Worker file and returning something else. The fix was taking .js out of the proxy's matcher. And a less silly one: lessons that time things (inserting at the start of an array versus at the end) used too many elements and hit the three-second wall on slow phones. They went down to twenty thousand.
What makes the course maintainable is a test: npm run aprende runs every program and every solution in the 48 lessons with the same executor the page uses, and fails if any of them doesn't print what the lesson says it prints. A programming course with an example that doesn't run is worse than no course.