stone-lang 0.1.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 (68) hide show
  1. package/README.md +52 -0
  2. package/StoneEngine.js +879 -0
  3. package/StoneEngineService.js +1727 -0
  4. package/adapters/FileSystemAdapter.js +230 -0
  5. package/adapters/OutputAdapter.js +208 -0
  6. package/adapters/index.js +6 -0
  7. package/cli/CLIOutputAdapter.js +196 -0
  8. package/cli/DaemonClient.js +349 -0
  9. package/cli/JSONOutputAdapter.js +135 -0
  10. package/cli/ReplSession.js +567 -0
  11. package/cli/ViewerServer.js +590 -0
  12. package/cli/commands/check.js +84 -0
  13. package/cli/commands/daemon.js +189 -0
  14. package/cli/commands/kill.js +66 -0
  15. package/cli/commands/package.js +713 -0
  16. package/cli/commands/ps.js +65 -0
  17. package/cli/commands/run.js +537 -0
  18. package/cli/entry.js +169 -0
  19. package/cli/index.js +14 -0
  20. package/cli/stonec.js +358 -0
  21. package/cli/test-compiler.js +181 -0
  22. package/cli/viewer/index.html +495 -0
  23. package/daemon/IPCServer.js +455 -0
  24. package/daemon/ProcessManager.js +327 -0
  25. package/daemon/ProcessRunner.js +307 -0
  26. package/daemon/daemon.js +398 -0
  27. package/daemon/index.js +16 -0
  28. package/frontend/analysis/index.js +5 -0
  29. package/frontend/analysis/livenessAnalyzer.js +568 -0
  30. package/frontend/analysis/treeShaker.js +265 -0
  31. package/frontend/index.js +20 -0
  32. package/frontend/parsing/astBuilder.js +2196 -0
  33. package/frontend/parsing/index.js +7 -0
  34. package/frontend/parsing/sonParser.js +592 -0
  35. package/frontend/parsing/stoneAstTypes.js +703 -0
  36. package/frontend/parsing/terminal-registry.js +435 -0
  37. package/frontend/parsing/tokenizer.js +692 -0
  38. package/frontend/type-checker/OverloadedFunctionType.js +43 -0
  39. package/frontend/type-checker/TypeEnvironment.js +165 -0
  40. package/frontend/type-checker/bidirectionalInference.js +149 -0
  41. package/frontend/type-checker/index.js +10 -0
  42. package/frontend/type-checker/moduleAnalysis.js +248 -0
  43. package/frontend/type-checker/operatorMappings.js +35 -0
  44. package/frontend/type-checker/overloadResolution.js +605 -0
  45. package/frontend/type-checker/typeChecker.js +452 -0
  46. package/frontend/type-checker/typeCompatibility.js +389 -0
  47. package/frontend/type-checker/visitors/controlFlow.js +483 -0
  48. package/frontend/type-checker/visitors/functions.js +604 -0
  49. package/frontend/type-checker/visitors/index.js +38 -0
  50. package/frontend/type-checker/visitors/literals.js +341 -0
  51. package/frontend/type-checker/visitors/modules.js +159 -0
  52. package/frontend/type-checker/visitors/operators.js +109 -0
  53. package/frontend/type-checker/visitors/statements.js +768 -0
  54. package/frontend/types/index.js +5 -0
  55. package/frontend/types/operatorMap.js +134 -0
  56. package/frontend/types/types.js +2046 -0
  57. package/frontend/utils/errorCollector.js +244 -0
  58. package/frontend/utils/index.js +5 -0
  59. package/frontend/utils/moduleResolver.js +479 -0
  60. package/package.json +50 -0
  61. package/packages/browserCache.js +359 -0
  62. package/packages/fetcher.js +236 -0
  63. package/packages/index.js +130 -0
  64. package/packages/lockfile.js +271 -0
  65. package/packages/manifest.js +291 -0
  66. package/packages/packageResolver.js +356 -0
  67. package/packages/resolver.js +310 -0
  68. package/packages/semver.js +635 -0
@@ -0,0 +1,349 @@
1
+ /**
2
+ * DaemonClient - High-level client for Stone daemon
3
+ *
4
+ * Provides a convenient API for CLI commands:
5
+ * - Auto-starts daemon if not running
6
+ * - Handles connection management
7
+ * - Streams script output to adapters
8
+ */
9
+
10
+ import { spawn } from 'child_process';
11
+ import { IPCClientConnection, getSocketPath } from '../daemon/IPCServer.js';
12
+ import path from 'path';
13
+ import { fileURLToPath } from 'url';
14
+
15
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
+
17
+ /**
18
+ * DaemonClient - Client for Stone daemon
19
+ */
20
+ export class DaemonClient {
21
+ constructor(options = {}) {
22
+ this.connection = null;
23
+ this.outputAdapter = options.outputAdapter || null;
24
+ this.autoStartDaemon = options.autoStartDaemon !== false;
25
+ this.daemonStartTimeout = options.daemonStartTimeout || 5000;
26
+ }
27
+
28
+ /**
29
+ * Connect to the daemon, starting it if necessary
30
+ */
31
+ async connect() {
32
+ if (this.connection && this.connection.isConnected()) {
33
+ return;
34
+ }
35
+
36
+ try {
37
+ this.connection = new IPCClientConnection();
38
+ await this.connection.connect();
39
+ } catch (err) {
40
+ // Daemon not running - try to start it
41
+ if (this.autoStartDaemon && err.code === 'ENOENT' || err.code === 'ECONNREFUSED') {
42
+ await this.startDaemon();
43
+ // Retry connection
44
+ this.connection = new IPCClientConnection();
45
+ await this.connection.connect();
46
+ } else {
47
+ throw err;
48
+ }
49
+ }
50
+
51
+ // Set up event handling
52
+ this.connection.on('event', (event) => {
53
+ this._handleEvent(event);
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Start the daemon as a background process
59
+ */
60
+ async startDaemon() {
61
+ const daemonPath = path.join(__dirname, '..', 'daemon', 'daemon.js');
62
+
63
+ return new Promise((resolve, reject) => {
64
+ // Spawn daemon in background
65
+ const daemon = spawn(process.execPath, [daemonPath], {
66
+ detached: true,
67
+ stdio: 'ignore',
68
+ env: { ...process.env },
69
+ });
70
+
71
+ daemon.unref();
72
+
73
+ // Wait for daemon to start
74
+ let attempts = 0;
75
+ const maxAttempts = 20;
76
+ const checkInterval = this.daemonStartTimeout / maxAttempts;
77
+
78
+ const checkDaemon = async () => {
79
+ attempts++;
80
+
81
+ try {
82
+ const testConn = new IPCClientConnection();
83
+ await testConn.connect();
84
+ testConn.disconnect();
85
+ resolve();
86
+ } catch (err) {
87
+ if (attempts >= maxAttempts) {
88
+ reject(new Error('Daemon failed to start'));
89
+ } else {
90
+ setTimeout(checkDaemon, checkInterval);
91
+ }
92
+ }
93
+ };
94
+
95
+ // Give daemon a moment to start before first check
96
+ setTimeout(checkDaemon, 200);
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Disconnect from the daemon
102
+ */
103
+ disconnect() {
104
+ if (this.connection) {
105
+ this.connection.disconnect();
106
+ this.connection = null;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Run a script
112
+ * @param {string} scriptPath - Path to script file
113
+ * @param {Object} options - Run options
114
+ * @returns {Promise<Object>} Execution result
115
+ */
116
+ async run(scriptPath, options = {}) {
117
+ await this.connect();
118
+
119
+ const response = await this.connection.request('run', {
120
+ script: scriptPath,
121
+ cwd: options.cwd || process.cwd(),
122
+ variables: options.variables || {},
123
+ });
124
+
125
+ if (response.error) {
126
+ throw new Error(response.error);
127
+ }
128
+
129
+ const processId = response.processId;
130
+
131
+ // Wait for process completion
132
+ return this._waitForProcess(processId);
133
+ }
134
+
135
+ /**
136
+ * Run inline source code
137
+ * @param {string} source - Stone source code
138
+ * @param {Object} options - Run options
139
+ * @returns {Promise<Object>} Execution result
140
+ */
141
+ async runSource(source, options = {}) {
142
+ await this.connect();
143
+
144
+ const response = await this.connection.request('run', {
145
+ source,
146
+ cwd: options.cwd || process.cwd(),
147
+ variables: options.variables || {},
148
+ });
149
+
150
+ if (response.error) {
151
+ throw new Error(response.error);
152
+ }
153
+
154
+ const processId = response.processId;
155
+
156
+ // Wait for process completion
157
+ return this._waitForProcess(processId);
158
+ }
159
+
160
+ /**
161
+ * Kill a running process
162
+ * @param {string} processId - Process ID
163
+ * @returns {Promise<boolean>} Whether kill succeeded
164
+ */
165
+ async kill(processId) {
166
+ await this.connect();
167
+
168
+ const response = await this.connection.request('kill', { processId });
169
+
170
+ if (response.error) {
171
+ throw new Error(response.error);
172
+ }
173
+
174
+ return response.killed;
175
+ }
176
+
177
+ /**
178
+ * List processes
179
+ * @param {Object} filter - Filter options { status, mine }
180
+ * @returns {Promise<Array>} List of processes
181
+ */
182
+ async list(filter = {}) {
183
+ await this.connect();
184
+
185
+ const response = await this.connection.request('list', filter);
186
+
187
+ if (response.error) {
188
+ throw new Error(response.error);
189
+ }
190
+
191
+ return response.processes;
192
+ }
193
+
194
+ /**
195
+ * Get daemon status
196
+ * @returns {Promise<Object>} Daemon status
197
+ */
198
+ async status() {
199
+ await this.connect();
200
+
201
+ const response = await this.connection.request('status');
202
+
203
+ if (response.error) {
204
+ throw new Error(response.error);
205
+ }
206
+
207
+ return response;
208
+ }
209
+
210
+ /**
211
+ * Shutdown the daemon
212
+ * @param {boolean} force - Force shutdown even if other clients connected
213
+ * @returns {Promise<boolean>}
214
+ */
215
+ async shutdown(force = false) {
216
+ await this.connect();
217
+
218
+ const response = await this.connection.request('shutdown', { force });
219
+
220
+ if (response.error) {
221
+ throw new Error(response.error);
222
+ }
223
+
224
+ return response.shutting_down;
225
+ }
226
+
227
+ /**
228
+ * Wait for a process to complete
229
+ * @param {string} processId - Process ID
230
+ * @returns {Promise<Object>} Process result
231
+ */
232
+ async _waitForProcess(processId) {
233
+ return new Promise((resolve, reject) => {
234
+ const handleEvent = (event) => {
235
+ if (event.processId !== processId) return;
236
+
237
+ switch (event.event) {
238
+ case 'process:end':
239
+ this.connection.off('event', handleEvent);
240
+ resolve({
241
+ success: event.success,
242
+ result: event.result,
243
+ error: event.error,
244
+ duration: event.duration,
245
+ });
246
+ break;
247
+
248
+ case 'process:completed':
249
+ this.connection.off('event', handleEvent);
250
+ resolve({
251
+ success: true,
252
+ result: event.result,
253
+ duration: event.duration,
254
+ });
255
+ break;
256
+
257
+ case 'process:failed':
258
+ this.connection.off('event', handleEvent);
259
+ resolve({
260
+ success: false,
261
+ error: event.error,
262
+ });
263
+ break;
264
+
265
+ case 'process:killed':
266
+ this.connection.off('event', handleEvent);
267
+ resolve({
268
+ success: false,
269
+ error: 'Process was killed',
270
+ killed: true,
271
+ });
272
+ break;
273
+ }
274
+ };
275
+
276
+ this.connection.on('event', handleEvent);
277
+ });
278
+ }
279
+
280
+ /**
281
+ * Handle events from daemon
282
+ */
283
+ _handleEvent(event) {
284
+ if (!this.outputAdapter) return;
285
+
286
+ switch (event.event) {
287
+ case 'print':
288
+ this.outputAdapter.print(event.value);
289
+ break;
290
+
291
+ case 'terminal:create':
292
+ this.outputAdapter.createTerminal(event.id, event.type, event.config);
293
+ break;
294
+
295
+ case 'terminal:add':
296
+ this.outputAdapter.addToTerminal(event.id, event.data);
297
+ break;
298
+
299
+ case 'terminal:set':
300
+ this.outputAdapter.setTerminal(event.id, event.data);
301
+ break;
302
+
303
+ case 'terminal:clear':
304
+ this.outputAdapter.clearTerminal(event.id);
305
+ break;
306
+
307
+ case 'error':
308
+ this.outputAdapter.error(event.message, event.location);
309
+ break;
310
+
311
+ case 'process:end':
312
+ this.outputAdapter.finish(event.success, event.result);
313
+ break;
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Set the output adapter
319
+ * @param {OutputAdapter} adapter
320
+ */
321
+ setOutputAdapter(adapter) {
322
+ this.outputAdapter = adapter;
323
+ }
324
+
325
+ /**
326
+ * Check if daemon is running
327
+ * @returns {Promise<boolean>}
328
+ */
329
+ static async isDaemonRunning() {
330
+ try {
331
+ const conn = new IPCClientConnection();
332
+ await conn.connect();
333
+ conn.disconnect();
334
+ return true;
335
+ } catch {
336
+ return false;
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Get the daemon socket path
342
+ * @returns {string}
343
+ */
344
+ static getSocketPath() {
345
+ return getSocketPath();
346
+ }
347
+ }
348
+
349
+ export default DaemonClient;
@@ -0,0 +1,135 @@
1
+ /**
2
+ * JSONOutputAdapter - JSON Lines streaming output
3
+ *
4
+ * Outputs Stone execution events as newline-delimited JSON (NDJSON).
5
+ * Useful for:
6
+ * - IDE integrations (VS Code, IntelliJ)
7
+ * - Desktop app communication (Riva)
8
+ * - CI/CD pipelines
9
+ * - Log processing
10
+ *
11
+ * Each line is a complete JSON object with a "type" field.
12
+ */
13
+
14
+ import { OutputAdapter } from '../adapters/OutputAdapter.js';
15
+
16
+ /**
17
+ * JSONOutputAdapter - Outputs events as JSON Lines
18
+ */
19
+ export class JSONOutputAdapter extends OutputAdapter {
20
+ constructor(options = {}) {
21
+ super();
22
+ this.stream = options.stream || process.stdout;
23
+ this.includeTimestamps = options.timestamps !== false;
24
+ this.processId = options.processId || null;
25
+
26
+ // Track terminals for final output
27
+ this.terminals = {};
28
+ }
29
+
30
+ _emit(event) {
31
+ if (this.includeTimestamps) {
32
+ event.timestamp = Date.now();
33
+ }
34
+ if (this.processId) {
35
+ event.processId = this.processId;
36
+ }
37
+ this.stream.write(JSON.stringify(event) + '\n');
38
+ }
39
+
40
+ print(text) {
41
+ this._emit({
42
+ type: 'print',
43
+ value: text,
44
+ });
45
+ }
46
+
47
+ createTerminal(id, type, config) {
48
+ this.terminals[id] = {
49
+ type,
50
+ config,
51
+ data: [],
52
+ };
53
+
54
+ this._emit({
55
+ type: 'terminal:create',
56
+ id,
57
+ terminalType: type,
58
+ config,
59
+ });
60
+ }
61
+
62
+ addToTerminal(id, data) {
63
+ const terminal = this.terminals[id];
64
+ if (terminal) {
65
+ const items = Array.isArray(data) ? data : [data];
66
+ terminal.data.push(...items);
67
+ }
68
+
69
+ this._emit({
70
+ type: 'terminal:add',
71
+ id,
72
+ data,
73
+ });
74
+ }
75
+
76
+ setTerminal(id, data) {
77
+ const terminal = this.terminals[id];
78
+ if (terminal) {
79
+ terminal.data = Array.isArray(data) ? data : [data];
80
+ }
81
+
82
+ this._emit({
83
+ type: 'terminal:set',
84
+ id,
85
+ data,
86
+ });
87
+ }
88
+
89
+ clearTerminal(id) {
90
+ const terminal = this.terminals[id];
91
+ if (terminal) {
92
+ terminal.data = [];
93
+ }
94
+
95
+ this._emit({
96
+ type: 'terminal:clear',
97
+ id,
98
+ });
99
+ }
100
+
101
+ error(message, location) {
102
+ this._emit({
103
+ type: 'error',
104
+ message,
105
+ location,
106
+ });
107
+ }
108
+
109
+ finish(success, result) {
110
+ this._emit({
111
+ type: 'finish',
112
+ success,
113
+ result: success ? result : undefined,
114
+ error: !success && result ? (result.message || String(result)) : undefined,
115
+ terminals: this.terminals,
116
+ });
117
+ }
118
+
119
+ progress(info) {
120
+ this._emit({
121
+ type: 'progress',
122
+ ...info,
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Get all terminal data
128
+ * @returns {Object}
129
+ */
130
+ getTerminals() {
131
+ return this.terminals;
132
+ }
133
+ }
134
+
135
+ export default JSONOutputAdapter;