nrepl-sci: سرور nREPL که کامل توی مرورگر اجرا میشه
خلاصهٔ کاملتر
سایت nrepl.org تازه بازطراحی شده و حالا یه بخش «Try it» داره که یه سرور واقعی nREPL (یعنی پروتکلی که ادیتورها باهاش به REPL زبان Clojure وصل میشن) توی خود مرورگر اجرا میکنه. نویسنده که نگهدارندهی nREPL هست، تو این مقاله توضیح میده چرا سرور باید توی مرورگر باشه: سروری که کد آدمهای ناشناس اینترنت رو اجرا کنه، هم خطرناکه و هم خرج داره.
اول CheerpJ (یه JVM که توی مرورگر اجرا میشه) رو امتحان کرده. nREPL 1.8 و Clojure 1.12 روش خوب کار میکردن، ولی حدود ۸ ثانیه طول میکشید تا آماده بشه، ۴.۵ مگابایت دانلود داشت و interrupt فقط روی کدِ در حال sleep کار میکرد. scittle و nbb هم به کارش نمیاومدن. آخرش nrepl-sci رو نوشته که روی SCI (مفسر Clojure از Michiel Borkent) ساخته شده، حدود ۲۳۰ کیلوبایت gzip شدهست و توی کسری از ثانیه بالا میاد.
به گفتهی نویسنده، بخش سخت دو تا op بود: interrupt و stdin. جاوااسکریپت یه thread داره، پس وقتی یه حلقهی بیپایان اجرا میشه، سرور حتی نمیتونه درخواست توقف رو بخونه. راهحل اینه که سرور توی دو تا Web Worker اجرا بشه: یه supervisor که درخواستها رو صف میکنه و به interrupt و stdin جواب میده، و یه evaluator که SCI رو اجرا میکنه.
این دو تا از طریق یه SharedArrayBuffer (حافظهی مشترک بین workerها) مثل صندوق پستی با هم حرف میزنن. evaluator با Atomics.wait بدون مصرف CPU منتظر میمونه، پس read-line مثل JVM منتظر ورودی میشه. SCI روی هر ۲۵۶ فراخوانی تابع یه hook صدا میزنه تا صندوق رو چک کنه. کدی که هیچوقت به SCI برنمیگرده، مثل یه regex گیرکرده، بعد از یه ثانیه با کشتن evaluator متوقف میشه و تعریفها از دست میرن.
مشکل اینه که مرورگر فقط به صفحههای cross-origin isolated (یعنی صفحههایی که هدرهای Cross-Origin-Opener-Policy و Cross-Origin-Embedder-Policy رو میفرستن) SharedArrayBuffer میده. GitHub Pages این هدرها رو نمیفرسته، پس اونجا read-line خطا میده و هر interrupt یعنی شروع از اول. برای تست، نویسنده از ابزار خودش proof استفاده کرده و nrepl-sci همهی ۷۳ چک مربوط رو پاس میکنه. بدون حافظهی مشترک فقط دو تا رد میشه.
محدودیتها هم هست: این SCI هست نه Clojure واقعی، کلاسهای Java نداره و interop با جاوااسکریپت بستهست، که همین باعث میشه جاسازیش امن باشه. برای استفاده، worker درخواستها رو به شکل bencode توی یه Uint8Array میگیره و جواب رو برمیگردونه. یه نسخهی Node هم داره (node nrepl-sci.js --port 7888) که حدود یکدهم ثانیه بالا میاد و برای تست کلاینتها به درد میخوره.
نکات کلیدی:
- nrepl-sci حدود ۱ مگابایت جاوااسکریپت (۲۳۰ کیلوبایت gzip) هست و روی مفسر SCI ساخته شده.
- CheerpJ حدود ۸ ثانیه زمان راهاندازی و ۴.۵ مگابایت حجم داشت.
- سرور با دو Web Worker و یه SharedArrayBuffer مشکل interrupt و stdin رو حل میکنه.
- بدون هدرهای COOP و COEP، فقط تستهای stdin.need-input و robust.sessions-at-once رد میشن.
- interop با جاوااسکریپت عمداً بستهست، پس (.toUpperCase "hi") کار نمیکنه.
- نسخهی Node با --port 7888 یه سرور nREPL معمولی روی socket میده.




