freelang-v4 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +548 -0
  2. package/dist/ast.d.ts +367 -0
  3. package/dist/ast.js +4 -0
  4. package/dist/ast.js.map +1 -0
  5. package/dist/async-basic.test.d.ts +1 -0
  6. package/dist/async-basic.test.js +88 -0
  7. package/dist/async-basic.test.js.map +1 -0
  8. package/dist/async-jest.test.d.ts +1 -0
  9. package/dist/async-jest.test.js +99 -0
  10. package/dist/async-jest.test.js.map +1 -0
  11. package/dist/channel-jest.test.d.ts +1 -0
  12. package/dist/channel-jest.test.js +148 -0
  13. package/dist/channel-jest.test.js.map +1 -0
  14. package/dist/checker-jest.test.d.ts +1 -0
  15. package/dist/checker-jest.test.js +160 -0
  16. package/dist/checker-jest.test.js.map +1 -0
  17. package/dist/checker.d.ts +149 -0
  18. package/dist/checker.js +1565 -0
  19. package/dist/checker.js.map +1 -0
  20. package/dist/checker.test.d.ts +1 -0
  21. package/dist/checker.test.js +217 -0
  22. package/dist/checker.test.js.map +1 -0
  23. package/dist/compiler-jest.test.d.ts +1 -0
  24. package/dist/compiler-jest.test.js +233 -0
  25. package/dist/compiler-jest.test.js.map +1 -0
  26. package/dist/compiler.d.ts +127 -0
  27. package/dist/compiler.js +1588 -0
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/compiler.test.d.ts +1 -0
  30. package/dist/compiler.test.js +313 -0
  31. package/dist/compiler.test.js.map +1 -0
  32. package/dist/db-100m-full.d.ts +5 -0
  33. package/dist/db-100m-full.js +78 -0
  34. package/dist/db-100m-full.js.map +1 -0
  35. package/dist/db-100m-no-index.d.ts +12 -0
  36. package/dist/db-100m-no-index.js +119 -0
  37. package/dist/db-100m-no-index.js.map +1 -0
  38. package/dist/db-100m-real.d.ts +5 -0
  39. package/dist/db-100m-real.js +131 -0
  40. package/dist/db-100m-real.js.map +1 -0
  41. package/dist/db-100m-streaming.d.ts +15 -0
  42. package/dist/db-100m-streaming.js +164 -0
  43. package/dist/db-100m-streaming.js.map +1 -0
  44. package/dist/db-100m-test.d.ts +5 -0
  45. package/dist/db-100m-test.js +111 -0
  46. package/dist/db-100m-test.js.map +1 -0
  47. package/dist/db-jest.test.d.ts +1 -0
  48. package/dist/db-jest.test.js +182 -0
  49. package/dist/db-jest.test.js.map +1 -0
  50. package/dist/db-runtime.d.ts +24 -0
  51. package/dist/db-runtime.js +204 -0
  52. package/dist/db-runtime.js.map +1 -0
  53. package/dist/db.d.ts +249 -0
  54. package/dist/db.js +593 -0
  55. package/dist/db.js.map +1 -0
  56. package/dist/file-io-jest.test.d.ts +1 -0
  57. package/dist/file-io-jest.test.js +225 -0
  58. package/dist/file-io-jest.test.js.map +1 -0
  59. package/dist/for-of-jest.test.d.ts +1 -0
  60. package/dist/for-of-jest.test.js +230 -0
  61. package/dist/for-of-jest.test.js.map +1 -0
  62. package/dist/for-of.test.d.ts +1 -0
  63. package/dist/for-of.test.js +305 -0
  64. package/dist/for-of.test.js.map +1 -0
  65. package/dist/function-literal-jest.test.d.ts +1 -0
  66. package/dist/function-literal-jest.test.js +180 -0
  67. package/dist/function-literal-jest.test.js.map +1 -0
  68. package/dist/function-literal.test.d.ts +1 -0
  69. package/dist/function-literal.test.js +245 -0
  70. package/dist/function-literal.test.js.map +1 -0
  71. package/dist/generics-jest.test.d.ts +1 -0
  72. package/dist/generics-jest.test.js +93 -0
  73. package/dist/generics-jest.test.js.map +1 -0
  74. package/dist/ir-gen.d.ts +15 -0
  75. package/dist/ir-gen.js +400 -0
  76. package/dist/ir-gen.js.map +1 -0
  77. package/dist/ir.d.ts +114 -0
  78. package/dist/ir.js +5 -0
  79. package/dist/ir.js.map +1 -0
  80. package/dist/lexer.d.ts +110 -0
  81. package/dist/lexer.js +467 -0
  82. package/dist/lexer.js.map +1 -0
  83. package/dist/lexer.test.d.ts +1 -0
  84. package/dist/lexer.test.js +426 -0
  85. package/dist/lexer.test.js.map +1 -0
  86. package/dist/main.d.ts +2 -0
  87. package/dist/main.js +241 -0
  88. package/dist/main.js.map +1 -0
  89. package/dist/module-jest.test.d.ts +1 -0
  90. package/dist/module-jest.test.js +123 -0
  91. package/dist/module-jest.test.js.map +1 -0
  92. package/dist/parser.d.ts +56 -0
  93. package/dist/parser.js +1060 -0
  94. package/dist/parser.js.map +1 -0
  95. package/dist/parser.test.d.ts +1 -0
  96. package/dist/parser.test.js +461 -0
  97. package/dist/parser.test.js.map +1 -0
  98. package/dist/pattern-matching-jest.test.d.ts +1 -0
  99. package/dist/pattern-matching-jest.test.js +158 -0
  100. package/dist/pattern-matching-jest.test.js.map +1 -0
  101. package/dist/pkg/init.d.ts +1 -0
  102. package/dist/pkg/init.js +118 -0
  103. package/dist/pkg/init.js.map +1 -0
  104. package/dist/pkg/install.d.ts +1 -0
  105. package/dist/pkg/install.js +77 -0
  106. package/dist/pkg/install.js.map +1 -0
  107. package/dist/pkg/registry.d.ts +23 -0
  108. package/dist/pkg/registry.js +106 -0
  109. package/dist/pkg/registry.js.map +1 -0
  110. package/dist/pkg/run.d.ts +1 -0
  111. package/dist/pkg/run.js +76 -0
  112. package/dist/pkg/run.js.map +1 -0
  113. package/dist/pkg/toml.d.ts +5 -0
  114. package/dist/pkg/toml.js +117 -0
  115. package/dist/pkg/toml.js.map +1 -0
  116. package/dist/repl.d.ts +15 -0
  117. package/dist/repl.js +197 -0
  118. package/dist/repl.js.map +1 -0
  119. package/dist/runtime/bytecode.d.ts +92 -0
  120. package/dist/runtime/bytecode.js +253 -0
  121. package/dist/runtime/bytecode.js.map +1 -0
  122. package/dist/runtime/value.d.ts +102 -0
  123. package/dist/runtime/value.js +302 -0
  124. package/dist/runtime/value.js.map +1 -0
  125. package/dist/runtime/vm.d.ts +65 -0
  126. package/dist/runtime/vm.js +293 -0
  127. package/dist/runtime/vm.js.map +1 -0
  128. package/dist/struct-instance-jest.test.d.ts +1 -0
  129. package/dist/struct-instance-jest.test.js +209 -0
  130. package/dist/struct-instance-jest.test.js.map +1 -0
  131. package/dist/struct-instance.test.d.ts +1 -0
  132. package/dist/struct-instance.test.js +291 -0
  133. package/dist/struct-instance.test.js.map +1 -0
  134. package/dist/struct-jest.test.d.ts +1 -0
  135. package/dist/struct-jest.test.js +176 -0
  136. package/dist/struct-jest.test.js.map +1 -0
  137. package/dist/struct.test.d.ts +1 -0
  138. package/dist/struct.test.js +231 -0
  139. package/dist/struct.test.js.map +1 -0
  140. package/dist/trait-jest.test.d.ts +1 -0
  141. package/dist/trait-jest.test.js +120 -0
  142. package/dist/trait-jest.test.js.map +1 -0
  143. package/dist/vm-jest.test.d.ts +1 -0
  144. package/dist/vm-jest.test.js +569 -0
  145. package/dist/vm-jest.test.js.map +1 -0
  146. package/dist/vm.d.ts +81 -0
  147. package/dist/vm.js +1956 -0
  148. package/dist/vm.js.map +1 -0
  149. package/dist/vm.test.d.ts +1 -0
  150. package/dist/vm.test.js +337 -0
  151. package/dist/vm.test.js.map +1 -0
  152. package/dist/web-repl/sandbox.d.ts +11 -0
  153. package/dist/web-repl/sandbox.js +76 -0
  154. package/dist/web-repl/sandbox.js.map +1 -0
  155. package/dist/web-repl/server.d.ts +1 -0
  156. package/dist/web-repl/server.js +111 -0
  157. package/dist/web-repl/server.js.map +1 -0
  158. package/dist/while-loop-jest.test.d.ts +1 -0
  159. package/dist/while-loop-jest.test.js +201 -0
  160. package/dist/while-loop-jest.test.js.map +1 -0
  161. package/dist/while-loop.test.d.ts +1 -0
  162. package/dist/while-loop.test.js +262 -0
  163. package/dist/while-loop.test.js.map +1 -0
  164. package/docs/EXPERIENCE.md +787 -0
  165. package/docs/README.md +175 -0
  166. package/docs/V1_V2_V3_ANALYSIS.md +107 -0
  167. package/docs/_config.yml +36 -0
  168. package/docs/api-reference.md +459 -0
  169. package/docs/architecture.md +470 -0
  170. package/docs/benchmarks.md +295 -0
  171. package/docs/comparison.md +454 -0
  172. package/docs/index.md +335 -0
  173. package/docs/language-completeness.md +228 -0
  174. package/docs/learning-guide.md +651 -0
  175. package/package.json +65 -0
  176. package/src/api/deploy_key.fl +294 -0
  177. package/src/api/issue.fl +302 -0
  178. package/src/api/org.fl +356 -0
  179. package/src/api/repo.fl +394 -0
  180. package/src/api/team.fl +299 -0
  181. package/src/api/user.fl +385 -0
  182. package/src/api/webhook.fl +273 -0
  183. package/src/ast.ts +158 -0
  184. package/src/async-basic.test.ts +94 -0
  185. package/src/async-jest.test.ts +107 -0
  186. package/src/channel-jest.test.ts +158 -0
  187. package/src/checker-jest.test.ts +189 -0
  188. package/src/checker.test.ts +279 -0
  189. package/src/checker.ts +1861 -0
  190. package/src/commands/analyze.fl +227 -0
  191. package/src/commands/auth.fl +315 -0
  192. package/src/commands/batch.fl +349 -0
  193. package/src/commands/config.fl +199 -0
  194. package/src/commands/deploy_key.fl +352 -0
  195. package/src/commands/issue.fl +275 -0
  196. package/src/commands/main.fl +492 -0
  197. package/src/commands/org.fl +425 -0
  198. package/src/commands/repo.fl +581 -0
  199. package/src/commands/team.fl +244 -0
  200. package/src/commands/user.fl +423 -0
  201. package/src/commands/webhook.fl +400 -0
  202. package/src/compiler-jest.test.ts +275 -0
  203. package/src/compiler.test.ts +375 -0
  204. package/src/compiler.ts +1770 -0
  205. package/src/config.fl +175 -0
  206. package/src/core/batch.fl +355 -0
  207. package/src/core/cache.fl +284 -0
  208. package/src/core/ensure.fl +324 -0
  209. package/src/db-100m-full.ts +96 -0
  210. package/src/db-100m-no-index.ts +133 -0
  211. package/src/db-100m-real.ts +152 -0
  212. package/src/db-100m-streaming.ts +154 -0
  213. package/src/db-100m-test.ts +136 -0
  214. package/src/db-jest.test.ts +161 -0
  215. package/src/db-runtime.ts +242 -0
  216. package/src/db.ts +676 -0
  217. package/src/errors.fl +134 -0
  218. package/src/for-of-jest.test.ts +246 -0
  219. package/src/for-of.test.ts +308 -0
  220. package/src/function-literal-jest.test.ts +193 -0
  221. package/src/function-literal.test.ts +248 -0
  222. package/src/generics-jest.test.ts +104 -0
  223. package/src/http/client.fl +327 -0
  224. package/src/ir-gen.ts +459 -0
  225. package/src/ir.ts +80 -0
  226. package/src/lexer.test.ts +499 -0
  227. package/src/lexer.ts +522 -0
  228. package/src/main.ts +223 -0
  229. package/src/models.fl +162 -0
  230. package/src/module-jest.test.ts +145 -0
  231. package/src/parser.test.ts +542 -0
  232. package/src/parser.ts +1211 -0
  233. package/src/pattern-matching-jest.test.ts +170 -0
  234. package/src/pkg/init.ts +91 -0
  235. package/src/pkg/install.ts +56 -0
  236. package/src/pkg/registry.ts +103 -0
  237. package/src/pkg/run.ts +49 -0
  238. package/src/pkg/toml.ts +129 -0
  239. package/src/repl.ts +190 -0
  240. package/src/runtime/bytecode.ts +291 -0
  241. package/src/runtime/value.ts +322 -0
  242. package/src/runtime/vm.ts +354 -0
  243. package/src/self-host/bootstrap.fl +68 -0
  244. package/src/self-host/interpreter.fl +361 -0
  245. package/src/self-host/lexer-simple.fl +22 -0
  246. package/src/self-host/lexer.fl +305 -0
  247. package/src/self-host/parser.fl +580 -0
  248. package/src/struct-instance-jest.test.ts +221 -0
  249. package/src/struct-instance.test.ts +293 -0
  250. package/src/struct-jest.test.ts +187 -0
  251. package/src/struct.test.ts +234 -0
  252. package/src/trait-jest.test.ts +136 -0
  253. package/src/vm-jest.test.ts +754 -0
  254. package/src/vm.ts +1976 -0
  255. package/src/web-repl/public/index.html +50 -0
  256. package/src/web-repl/public/main.js +105 -0
  257. package/src/web-repl/public/style.css +225 -0
  258. package/src/web-repl/sandbox.ts +88 -0
  259. package/src/web-repl/server.ts +97 -0
  260. package/src/while-loop-jest.test.ts +218 -0
  261. package/src/while-loop.test.ts +267 -0
@@ -0,0 +1,50 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FreeLang v4 Web REPL</title>
7
+ <link rel="stylesheet" href="./style.css" />
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.3.0/xterm.min.css" />
9
+ </head>
10
+ <body>
11
+ <div class="container">
12
+ <div class="header">
13
+ <h1>🦁 FreeLang v4 Web REPL</h1>
14
+ <p>Interactive FreeLang coding environment</p>
15
+ </div>
16
+
17
+ <div class="main">
18
+ <div class="editor-section">
19
+ <div class="editor-header">
20
+ <h2>Code Editor</h2>
21
+ <button id="btn-clear" class="btn btn-secondary">Clear</button>
22
+ </div>
23
+ <textarea id="code-input" class="code-input" placeholder="Enter FreeLang code here..."></textarea>
24
+ <div class="editor-footer">
25
+ <button id="btn-run" class="btn btn-primary">Run (Ctrl+Enter)</button>
26
+ <span id="status" class="status"></span>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="output-section">
31
+ <div class="output-header">
32
+ <h2>Output</h2>
33
+ </div>
34
+ <div id="terminal" class="terminal"></div>
35
+ </div>
36
+ </div>
37
+
38
+ <div class="footer">
39
+ <p>🔒 Sandbox environment with 5-second timeout limit</p>
40
+ <p>
41
+ <a href="https://github.com/FreeLang/freelang-v4" target="_blank">GitHub</a> •
42
+ <a href="https://gogs.dclub.kr/kim/freelang-v4" target="_blank">Gogs</a>
43
+ </p>
44
+ </div>
45
+ </div>
46
+
47
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.3.0/xterm.min.js"></script>
48
+ <script src="./main.js"></script>
49
+ </body>
50
+ </html>
@@ -0,0 +1,105 @@
1
+ // Web REPL Client — WebSocket communication with xterm
2
+
3
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
4
+ const ws = new WebSocket(`${protocol}//${window.location.host}`);
5
+
6
+ const term = new Terminal({
7
+ cursorBlink: true,
8
+ fontSize: 14,
9
+ fontFamily: "Monaco, Menlo, 'Ubuntu Mono', monospace",
10
+ });
11
+
12
+ const codeInput = document.getElementById("code-input");
13
+ const btnRun = document.getElementById("btn-run");
14
+ const btnClear = document.getElementById("btn-clear");
15
+ const status = document.getElementById("status");
16
+
17
+ // Mount terminal to DOM
18
+ term.open(document.getElementById("terminal"));
19
+
20
+ term.writeln("🦁 FreeLang v4 Web REPL");
21
+ term.writeln("Type your code in the editor and click 'Run' to execute");
22
+ term.writeln("");
23
+
24
+ // WebSocket event handlers
25
+ ws.onopen = () => {
26
+ updateStatus("Connected", "success");
27
+ term.writeln("✓ Connected to server");
28
+ term.writeln("");
29
+ };
30
+
31
+ ws.onmessage = (event) => {
32
+ const response = JSON.parse(event.data);
33
+
34
+ if (response.type === "pong") {
35
+ // Ping/pong keep-alive
36
+ return;
37
+ }
38
+
39
+ if (response.type === "result") {
40
+ term.write(response.output || "");
41
+ term.writeln("");
42
+ updateStatus("Ready", "success");
43
+ } else if (response.type === "error") {
44
+ term.write(`\x1b[31m❌ Error: ${response.error}\x1b[0m\n`);
45
+ updateStatus("Error", "error");
46
+ }
47
+
48
+ term.write("> ");
49
+ };
50
+
51
+ ws.onerror = (error) => {
52
+ updateStatus("Error", "error");
53
+ term.writeln(`\x1b[31m❌ WebSocket error\x1b[0m`);
54
+ };
55
+
56
+ ws.onclose = () => {
57
+ updateStatus("Disconnected", "error");
58
+ term.writeln("\x1b[31m❌ Disconnected from server\x1b[0m");
59
+ };
60
+
61
+ // Button handlers
62
+ btnRun.addEventListener("click", () => {
63
+ const code = codeInput.value.trim();
64
+ if (code) {
65
+ executeCode(code);
66
+ }
67
+ });
68
+
69
+ btnClear.addEventListener("click", () => {
70
+ codeInput.value = "";
71
+ ws.send(JSON.stringify({ type: "clear" }));
72
+ term.clear();
73
+ term.writeln("> ");
74
+ });
75
+
76
+ // Keyboard shortcuts
77
+ codeInput.addEventListener("keydown", (e) => {
78
+ if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
79
+ e.preventDefault();
80
+ btnRun.click();
81
+ }
82
+ });
83
+
84
+ // Helper functions
85
+ function executeCode(code) {
86
+ updateStatus("Running...", "loading");
87
+ term.write("> ");
88
+ term.writeln(code.split("\n").join("\n> "));
89
+ ws.send(JSON.stringify({ type: "eval", code }));
90
+ }
91
+
92
+ function updateStatus(message, className) {
93
+ status.textContent = message;
94
+ status.className = `status ${className}`;
95
+ }
96
+
97
+ // Keep-alive ping
98
+ setInterval(() => {
99
+ if (ws.readyState === WebSocket.OPEN) {
100
+ ws.send(JSON.stringify({ type: "ping" }));
101
+ }
102
+ }, 30000);
103
+
104
+ // Show initial prompt
105
+ term.write("> ");
@@ -0,0 +1,225 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ html, body {
8
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
9
+ background: #1e1e1e;
10
+ color: #e0e0e0;
11
+ height: 100%;
12
+ }
13
+
14
+ .container {
15
+ display: flex;
16
+ flex-direction: column;
17
+ height: 100vh;
18
+ }
19
+
20
+ .header {
21
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
22
+ color: white;
23
+ padding: 20px 40px;
24
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
25
+ }
26
+
27
+ .header h1 {
28
+ font-size: 32px;
29
+ margin-bottom: 5px;
30
+ }
31
+
32
+ .header p {
33
+ font-size: 14px;
34
+ opacity: 0.9;
35
+ }
36
+
37
+ .main {
38
+ display: flex;
39
+ flex: 1;
40
+ gap: 20px;
41
+ padding: 20px;
42
+ overflow: hidden;
43
+ }
44
+
45
+ .editor-section, .output-section {
46
+ flex: 1;
47
+ display: flex;
48
+ flex-direction: column;
49
+ background: #252525;
50
+ border: 1px solid #3a3a3a;
51
+ border-radius: 8px;
52
+ overflow: hidden;
53
+ }
54
+
55
+ .editor-header, .output-header {
56
+ background: #2a2a2a;
57
+ padding: 12px 16px;
58
+ border-bottom: 1px solid #3a3a3a;
59
+ display: flex;
60
+ justify-content: space-between;
61
+ align-items: center;
62
+ }
63
+
64
+ .editor-header h2, .output-header h2 {
65
+ font-size: 16px;
66
+ font-weight: 600;
67
+ }
68
+
69
+ .code-input {
70
+ flex: 1;
71
+ padding: 16px;
72
+ background: #1e1e1e;
73
+ color: #e0e0e0;
74
+ border: none;
75
+ font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
76
+ font-size: 14px;
77
+ line-height: 1.6;
78
+ resize: none;
79
+ outline: none;
80
+ }
81
+
82
+ .code-input::placeholder {
83
+ color: #666;
84
+ }
85
+
86
+ .editor-footer {
87
+ background: #2a2a2a;
88
+ padding: 12px 16px;
89
+ border-top: 1px solid #3a3a3a;
90
+ display: flex;
91
+ justify-content: space-between;
92
+ align-items: center;
93
+ gap: 12px;
94
+ }
95
+
96
+ .btn {
97
+ padding: 8px 16px;
98
+ border: none;
99
+ border-radius: 4px;
100
+ font-size: 14px;
101
+ font-weight: 500;
102
+ cursor: pointer;
103
+ transition: all 0.2s ease;
104
+ }
105
+
106
+ .btn-primary {
107
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
108
+ color: white;
109
+ }
110
+
111
+ .btn-primary:hover {
112
+ transform: translateY(-2px);
113
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
114
+ }
115
+
116
+ .btn-primary:active {
117
+ transform: translateY(0);
118
+ }
119
+
120
+ .btn-secondary {
121
+ background: #3a3a3a;
122
+ color: #e0e0e0;
123
+ }
124
+
125
+ .btn-secondary:hover {
126
+ background: #4a4a4a;
127
+ }
128
+
129
+ .terminal {
130
+ flex: 1;
131
+ overflow: auto;
132
+ padding: 16px;
133
+ font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
134
+ font-size: 14px;
135
+ line-height: 1.6;
136
+ }
137
+
138
+ .terminal .xterm {
139
+ padding: 0 !important;
140
+ }
141
+
142
+ .status {
143
+ font-size: 12px;
144
+ padding: 4px 8px;
145
+ border-radius: 4px;
146
+ background: #3a3a3a;
147
+ color: #999;
148
+ }
149
+
150
+ .status.success {
151
+ color: #4ec9b0;
152
+ background: #0a3a2a;
153
+ }
154
+
155
+ .status.error {
156
+ color: #ce9178;
157
+ background: #3a0a0a;
158
+ }
159
+
160
+ .status.loading {
161
+ color: #dcdcaa;
162
+ background: #3a3a0a;
163
+ }
164
+
165
+ .footer {
166
+ background: #1e1e1e;
167
+ border-top: 1px solid #3a3a3a;
168
+ padding: 16px 40px;
169
+ text-align: center;
170
+ font-size: 12px;
171
+ color: #666;
172
+ }
173
+
174
+ .footer p {
175
+ margin: 4px 0;
176
+ }
177
+
178
+ .footer a {
179
+ color: #667eea;
180
+ text-decoration: none;
181
+ transition: color 0.2s ease;
182
+ }
183
+
184
+ .footer a:hover {
185
+ color: #764ba2;
186
+ }
187
+
188
+ /* Responsive design */
189
+ @media (max-width: 1024px) {
190
+ .main {
191
+ flex-direction: column;
192
+ gap: 16px;
193
+ }
194
+
195
+ .editor-section {
196
+ height: 300px;
197
+ }
198
+
199
+ .output-section {
200
+ flex: 1;
201
+ }
202
+ }
203
+
204
+ @media (max-width: 768px) {
205
+ .header {
206
+ padding: 16px 20px;
207
+ }
208
+
209
+ .header h1 {
210
+ font-size: 24px;
211
+ }
212
+
213
+ .main {
214
+ padding: 12px;
215
+ gap: 12px;
216
+ }
217
+
218
+ .editor-section {
219
+ height: 250px;
220
+ }
221
+
222
+ .footer {
223
+ padding: 12px 20px;
224
+ }
225
+ }
@@ -0,0 +1,88 @@
1
+ // SandboxedREPL — Secure execution with restrictions
2
+
3
+ import { Lexer } from "../lexer";
4
+ import { Parser } from "../parser";
5
+ import { TypeChecker } from "../checker";
6
+ import { Compiler } from "../compiler";
7
+ import { VM } from "../vm";
8
+ import { Chunk } from "../compiler";
9
+
10
+ // Forbidden builtins in sandboxed environment
11
+ const FORBIDDEN_BUILTINS = new Set([
12
+ "read_file",
13
+ "write_file",
14
+ "sqlite_open",
15
+ "pg_connect",
16
+ "mysql_connect",
17
+ ]);
18
+
19
+ export class SandboxedREPL {
20
+ private output: string[] = [];
21
+ private history: string[] = [];
22
+ private vm: VM;
23
+
24
+ constructor() {
25
+ this.vm = new VM();
26
+ }
27
+
28
+ async eval(code: string): Promise<void> {
29
+ this.output = [];
30
+
31
+ // Parse and compile
32
+ try {
33
+ const lexer = new Lexer(code);
34
+ const lexResult = lexer.tokenize();
35
+ const tokens = lexResult.tokens;
36
+
37
+ const parser = new Parser(tokens);
38
+ const parseResult = parser.parse();
39
+ const ast = parseResult.program;
40
+
41
+ // Check for forbidden builtins
42
+ this.checkForbiddenBuiltins(ast);
43
+
44
+ // Type check
45
+ const checker = new TypeChecker();
46
+ checker.check(ast);
47
+
48
+ // Compile
49
+ const compiler = new Compiler();
50
+ const chunk: Chunk = compiler.compile(ast);
51
+
52
+ // Execute with timeout
53
+ const result = await this.vm.run(chunk);
54
+
55
+ if (result.error) {
56
+ this.output.push(`Error: ${result.error}`);
57
+ } else {
58
+ this.output.push(...result.output);
59
+ }
60
+
61
+ this.history.push(code);
62
+ } catch (err: any) {
63
+ this.output.push(`Error: ${err.message || String(err)}`);
64
+ }
65
+ }
66
+
67
+ private checkForbiddenBuiltins(ast: any): void {
68
+ const code = JSON.stringify(ast);
69
+ for (const builtin of FORBIDDEN_BUILTINS) {
70
+ if (code.includes(`"${builtin}"`)) {
71
+ throw new Error(`Forbidden builtin: ${builtin}`);
72
+ }
73
+ }
74
+ }
75
+
76
+ getOutput(): string {
77
+ return this.output.join("\n");
78
+ }
79
+
80
+ getHistory(): string[] {
81
+ return this.history;
82
+ }
83
+
84
+ clear(): void {
85
+ this.output = [];
86
+ this.history = [];
87
+ }
88
+ }
@@ -0,0 +1,97 @@
1
+ // Web REPL Server — Express + WebSocket
2
+
3
+ import express from "express";
4
+ import * as http from "http";
5
+ import * as ws from "ws";
6
+ import * as path from "path";
7
+ import { SandboxedREPL } from "./sandbox";
8
+
9
+ const TIMEOUT_MS = 5000;
10
+
11
+ interface WSMessage {
12
+ type: "eval" | "clear" | "ping";
13
+ code?: string;
14
+ }
15
+
16
+ interface WSResponse {
17
+ type: "result" | "error" | "pong";
18
+ output?: string;
19
+ error?: string;
20
+ }
21
+
22
+ export async function startWebRepl(port: number): Promise<void> {
23
+ const app = express();
24
+ const server = http.createServer(app);
25
+ const wss = new ws.Server({ server });
26
+
27
+ // Serve static files
28
+ const publicDir = path.join(__dirname, "../web-repl/public");
29
+ app.use(express.static(publicDir));
30
+
31
+ // WebSocket connection handler
32
+ wss.on("connection", (socket: ws.WebSocket) => {
33
+ const repl = new SandboxedREPL();
34
+ console.log("[REPL] Client connected");
35
+
36
+ socket.on("message", async (data: string) => {
37
+ try {
38
+ const msg: WSMessage = JSON.parse(data);
39
+
40
+ if (msg.type === "ping") {
41
+ socket.send(JSON.stringify({ type: "pong" }));
42
+ return;
43
+ }
44
+
45
+ if (msg.type === "clear") {
46
+ repl.clear();
47
+ socket.send(
48
+ JSON.stringify({
49
+ type: "result",
50
+ output: "REPL cleared",
51
+ }),
52
+ );
53
+ return;
54
+ }
55
+
56
+ if (msg.type === "eval" && msg.code) {
57
+ const response: WSResponse = {
58
+ type: "result",
59
+ };
60
+
61
+ const timeoutPromise = new Promise<void>((_, reject) => {
62
+ setTimeout(() => reject(new Error("Execution timeout (5s)")), TIMEOUT_MS);
63
+ });
64
+
65
+ try {
66
+ await Promise.race([timeoutPromise, repl.eval(msg.code)]);
67
+ response.output = repl.getOutput();
68
+ } catch (err: any) {
69
+ response.type = "error";
70
+ response.error = err.message || String(err);
71
+ }
72
+
73
+ socket.send(JSON.stringify(response));
74
+ }
75
+ } catch (err: any) {
76
+ const response: WSResponse = {
77
+ type: "error",
78
+ error: err.message || "Invalid message format",
79
+ };
80
+ socket.send(JSON.stringify(response));
81
+ }
82
+ });
83
+
84
+ socket.on("close", () => {
85
+ console.log("[REPL] Client disconnected");
86
+ });
87
+
88
+ socket.on("error", (err: Error) => {
89
+ console.error("[REPL] WebSocket error:", err.message);
90
+ });
91
+ });
92
+
93
+ server.listen(port, () => {
94
+ console.log(`🌐 Web REPL running at http://localhost:${port}`);
95
+ console.log(` Open in browser to start coding!`);
96
+ });
97
+ }