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.
- package/README.md +52 -0
- package/StoneEngine.js +879 -0
- package/StoneEngineService.js +1727 -0
- package/adapters/FileSystemAdapter.js +230 -0
- package/adapters/OutputAdapter.js +208 -0
- package/adapters/index.js +6 -0
- package/cli/CLIOutputAdapter.js +196 -0
- package/cli/DaemonClient.js +349 -0
- package/cli/JSONOutputAdapter.js +135 -0
- package/cli/ReplSession.js +567 -0
- package/cli/ViewerServer.js +590 -0
- package/cli/commands/check.js +84 -0
- package/cli/commands/daemon.js +189 -0
- package/cli/commands/kill.js +66 -0
- package/cli/commands/package.js +713 -0
- package/cli/commands/ps.js +65 -0
- package/cli/commands/run.js +537 -0
- package/cli/entry.js +169 -0
- package/cli/index.js +14 -0
- package/cli/stonec.js +358 -0
- package/cli/test-compiler.js +181 -0
- package/cli/viewer/index.html +495 -0
- package/daemon/IPCServer.js +455 -0
- package/daemon/ProcessManager.js +327 -0
- package/daemon/ProcessRunner.js +307 -0
- package/daemon/daemon.js +398 -0
- package/daemon/index.js +16 -0
- package/frontend/analysis/index.js +5 -0
- package/frontend/analysis/livenessAnalyzer.js +568 -0
- package/frontend/analysis/treeShaker.js +265 -0
- package/frontend/index.js +20 -0
- package/frontend/parsing/astBuilder.js +2196 -0
- package/frontend/parsing/index.js +7 -0
- package/frontend/parsing/sonParser.js +592 -0
- package/frontend/parsing/stoneAstTypes.js +703 -0
- package/frontend/parsing/terminal-registry.js +435 -0
- package/frontend/parsing/tokenizer.js +692 -0
- package/frontend/type-checker/OverloadedFunctionType.js +43 -0
- package/frontend/type-checker/TypeEnvironment.js +165 -0
- package/frontend/type-checker/bidirectionalInference.js +149 -0
- package/frontend/type-checker/index.js +10 -0
- package/frontend/type-checker/moduleAnalysis.js +248 -0
- package/frontend/type-checker/operatorMappings.js +35 -0
- package/frontend/type-checker/overloadResolution.js +605 -0
- package/frontend/type-checker/typeChecker.js +452 -0
- package/frontend/type-checker/typeCompatibility.js +389 -0
- package/frontend/type-checker/visitors/controlFlow.js +483 -0
- package/frontend/type-checker/visitors/functions.js +604 -0
- package/frontend/type-checker/visitors/index.js +38 -0
- package/frontend/type-checker/visitors/literals.js +341 -0
- package/frontend/type-checker/visitors/modules.js +159 -0
- package/frontend/type-checker/visitors/operators.js +109 -0
- package/frontend/type-checker/visitors/statements.js +768 -0
- package/frontend/types/index.js +5 -0
- package/frontend/types/operatorMap.js +134 -0
- package/frontend/types/types.js +2046 -0
- package/frontend/utils/errorCollector.js +244 -0
- package/frontend/utils/index.js +5 -0
- package/frontend/utils/moduleResolver.js +479 -0
- package/package.json +50 -0
- package/packages/browserCache.js +359 -0
- package/packages/fetcher.js +236 -0
- package/packages/index.js +130 -0
- package/packages/lockfile.js +271 -0
- package/packages/manifest.js +291 -0
- package/packages/packageResolver.js +356 -0
- package/packages/resolver.js +310 -0
- 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;
|