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,398 @@
1
+ /**
2
+ * Stone Daemon - Central process manager for Stone scripts
3
+ *
4
+ * The daemon provides:
5
+ * - Process management (start, kill, list scripts)
6
+ * - Streaming output to connected clients
7
+ * - Automatic cleanup when clients disconnect
8
+ * - Auto-shutdown when idle (no clients, no running processes)
9
+ *
10
+ * Usage:
11
+ * node daemon.js [--foreground] [--no-auto-shutdown]
12
+ *
13
+ * CLI clients connect via IPC to run scripts:
14
+ * stone run script.stn -> connects to daemon, streams output
15
+ */
16
+
17
+ import { IPCServer } from './IPCServer.js';
18
+ import { ProcessManager } from './ProcessManager.js';
19
+ import { ProcessRunner, runProcess } from './ProcessRunner.js';
20
+
21
+ const DEFAULT_IDLE_TIMEOUT = 60000; // 1 minute with no activity = shutdown
22
+
23
+ /**
24
+ * StoneDaemon - Main daemon class
25
+ */
26
+ export class StoneDaemon {
27
+ constructor(options = {}) {
28
+ this.ipc = new IPCServer(options);
29
+ this.processManager = new ProcessManager();
30
+
31
+ this.autoShutdown = options.autoShutdown !== false;
32
+ this.idleTimeout = options.idleTimeout || DEFAULT_IDLE_TIMEOUT;
33
+ this._shutdownTimer = null;
34
+ this._isShuttingDown = false;
35
+
36
+ this._setupMessageHandlers();
37
+ this._setupEventForwarding();
38
+ }
39
+
40
+ /**
41
+ * Start the daemon
42
+ */
43
+ async start() {
44
+ console.log('Starting Stone daemon...');
45
+
46
+ try {
47
+ await this.ipc.start();
48
+ console.log(`Stone daemon listening on: ${this.ipc.getSocketPath()}`);
49
+
50
+ // Start idle timer
51
+ this._resetIdleTimer();
52
+
53
+ // Handle process signals
54
+ process.on('SIGINT', () => this.shutdown());
55
+ process.on('SIGTERM', () => this.shutdown());
56
+
57
+ } catch (err) {
58
+ console.error('Failed to start daemon:', err.message);
59
+ process.exit(1);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Shutdown the daemon
65
+ */
66
+ async shutdown() {
67
+ if (this._isShuttingDown) return;
68
+ this._isShuttingDown = true;
69
+
70
+ console.log('Shutting down Stone daemon...');
71
+
72
+ // Clear idle timer
73
+ if (this._shutdownTimer) {
74
+ clearTimeout(this._shutdownTimer);
75
+ }
76
+
77
+ // Kill all running processes
78
+ const stats = this.processManager.getProcessStats();
79
+ if (stats.running > 0) {
80
+ console.log(`Killing ${stats.running} running processes...`);
81
+ for (const proc of this.processManager.listProcesses({ status: 'running' })) {
82
+ this.processManager.killProcess(proc.id);
83
+ }
84
+ }
85
+
86
+ // Stop IPC server
87
+ await this.ipc.stop();
88
+
89
+ console.log('Stone daemon stopped.');
90
+ process.exit(0);
91
+ }
92
+
93
+ _setupMessageHandlers() {
94
+ // Run a script
95
+ this.ipc.onMessage('run', async (client, message) => {
96
+ return this._handleRun(client, message);
97
+ });
98
+
99
+ // Kill a process
100
+ this.ipc.onMessage('kill', async (client, message) => {
101
+ return this._handleKill(client, message);
102
+ });
103
+
104
+ // List processes
105
+ this.ipc.onMessage('list', async (client, message) => {
106
+ return this._handleList(client, message);
107
+ });
108
+
109
+ // Get process info
110
+ this.ipc.onMessage('info', async (client, message) => {
111
+ return this._handleInfo(client, message);
112
+ });
113
+
114
+ // Subscribe to process events (for processes not owned by this client)
115
+ this.ipc.onMessage('subscribe', async (client, message) => {
116
+ return this._handleSubscribe(client, message);
117
+ });
118
+
119
+ // Ping (keepalive)
120
+ this.ipc.onMessage('ping', async () => {
121
+ return { pong: true, time: Date.now() };
122
+ });
123
+
124
+ // Get daemon status
125
+ this.ipc.onMessage('status', async () => {
126
+ return {
127
+ status: 'running',
128
+ clients: this.ipc.getClientCount(),
129
+ processes: this.processManager.getProcessStats(),
130
+ socketPath: this.ipc.getSocketPath(),
131
+ uptime: process.uptime(),
132
+ };
133
+ });
134
+
135
+ // Shutdown daemon
136
+ this.ipc.onMessage('shutdown', async (client, message) => {
137
+ // Only allow shutdown if no other clients are connected
138
+ // or if force flag is set
139
+ if (this.ipc.getClientCount() > 1 && !message.force) {
140
+ return { error: 'Other clients are connected. Use --force to override.' };
141
+ }
142
+
143
+ // Schedule shutdown (don't await - respond first)
144
+ setImmediate(() => this.shutdown());
145
+ return { shutting_down: true };
146
+ });
147
+ }
148
+
149
+ _setupEventForwarding() {
150
+ // Client connected
151
+ this.ipc.on('connection', (client) => {
152
+ console.log(`Client connected: ${client.clientId}`);
153
+ this._resetIdleTimer();
154
+ });
155
+
156
+ // Client disconnected
157
+ this.ipc.on('disconnect', (client) => {
158
+ console.log(`Client disconnected: ${client.clientId}`);
159
+
160
+ // Kill processes owned by this client
161
+ const killed = this.processManager.killProcessesByOwner(client.clientId);
162
+ if (killed.length > 0) {
163
+ console.log(`Killed ${killed.length} processes owned by ${client.clientId}`);
164
+ }
165
+
166
+ // Check for auto-shutdown
167
+ this._checkIdleShutdown();
168
+ });
169
+
170
+ // Process events - broadcast process:created to ALL clients so they can subscribe
171
+ this.processManager.on('process:created', (proc) => {
172
+ // Broadcast to ALL clients (not just subscribers) so desktop can auto-subscribe
173
+ this.ipc.broadcast({
174
+ event: 'process:created',
175
+ processId: proc.id,
176
+ process: proc.toJSON(),
177
+ });
178
+ });
179
+
180
+ this.processManager.on('process:started', (proc) => {
181
+ this.ipc.sendToSubscribers(proc.id, {
182
+ event: 'process:started',
183
+ processId: proc.id,
184
+ });
185
+ });
186
+
187
+ this.processManager.on('process:completed', (proc) => {
188
+ this.ipc.sendToSubscribers(proc.id, {
189
+ event: 'process:completed',
190
+ processId: proc.id,
191
+ result: proc.result,
192
+ duration: proc.getDuration(),
193
+ });
194
+ this._resetIdleTimer();
195
+ });
196
+
197
+ this.processManager.on('process:failed', (proc) => {
198
+ this.ipc.sendToSubscribers(proc.id, {
199
+ event: 'process:failed',
200
+ processId: proc.id,
201
+ error: proc.error,
202
+ });
203
+ this._resetIdleTimer();
204
+ });
205
+
206
+ this.processManager.on('process:killed', (proc) => {
207
+ this.ipc.sendToSubscribers(proc.id, {
208
+ event: 'process:killed',
209
+ processId: proc.id,
210
+ });
211
+ this._resetIdleTimer();
212
+ });
213
+ }
214
+
215
+ async _handleRun(client, message) {
216
+ const { script, source, cwd, variables } = message;
217
+
218
+ if (!script && !source) {
219
+ throw new Error('Either script path or source is required');
220
+ }
221
+
222
+ // Create process entry
223
+ const proc = this.processManager.createProcess(
224
+ script || '<inline>',
225
+ client.clientId,
226
+ { cwd, variables }
227
+ );
228
+
229
+ // Mark as running
230
+ this.processManager.startProcess(proc.id);
231
+ client.addOwnedProcess(proc.id);
232
+
233
+ // Create runner and set up event forwarding to subscribers
234
+ const runner = new ProcessRunner(proc, {
235
+ scriptPath: script,
236
+ source: source,
237
+ cwd: cwd || process.cwd(),
238
+ });
239
+
240
+ // Forward runner events to all subscribed clients (owner + subscribers)
241
+ runner.on('event', (event) => {
242
+ this.ipc.sendToSubscribers(proc.id, event);
243
+ });
244
+
245
+ // Link runner to process for kill support
246
+ proc.runner = runner;
247
+
248
+ // Run async
249
+ runner.run().then((result) => {
250
+ if (result.success) {
251
+ this.processManager.completeProcess(proc.id, result.result, result.terminalsData);
252
+ } else {
253
+ this.processManager.failProcess(proc.id, result.error);
254
+ }
255
+ }).catch((err) => {
256
+ this.processManager.failProcess(proc.id, err.message);
257
+ });
258
+
259
+ // Return immediately with process info
260
+ return { processId: proc.id, status: 'started' };
261
+ }
262
+
263
+ async _handleKill(client, message) {
264
+ const { processId } = message;
265
+
266
+ if (!processId) {
267
+ throw new Error('Process ID is required');
268
+ }
269
+
270
+ const success = this.processManager.killProcess(processId, client.clientId);
271
+
272
+ if (success) {
273
+ client.removeOwnedProcess(processId);
274
+ return { killed: true, processId };
275
+ } else {
276
+ return { killed: false, processId, reason: 'Process not running or not found' };
277
+ }
278
+ }
279
+
280
+ async _handleList(client, message) {
281
+ const { status, mine } = message;
282
+
283
+ const filter = {};
284
+ if (status) filter.status = status;
285
+ if (mine) filter.owner = client.clientId;
286
+
287
+ const processes = this.processManager.listProcesses(filter);
288
+
289
+ return {
290
+ processes: processes.map(p => p.toJSON()),
291
+ count: processes.length,
292
+ };
293
+ }
294
+
295
+ async _handleInfo(client, message) {
296
+ const { processId } = message;
297
+
298
+ if (!processId) {
299
+ throw new Error('Process ID is required');
300
+ }
301
+
302
+ const proc = this.processManager.getProcess(processId);
303
+ if (!proc) {
304
+ return { found: false, processId };
305
+ }
306
+
307
+ return { found: true, process: proc.toJSON() };
308
+ }
309
+
310
+ async _handleSubscribe(client, message) {
311
+ const { processId } = message;
312
+
313
+ if (!processId) {
314
+ throw new Error('Process ID is required');
315
+ }
316
+
317
+ const proc = this.processManager.getProcess(processId);
318
+ if (!proc) {
319
+ return { subscribed: false, reason: 'Process not found' };
320
+ }
321
+
322
+ // Add subscription for this client to receive events for this process
323
+ client.addSubscription(processId);
324
+
325
+ // If process is already complete, send current state including terminalsData
326
+ if (proc.status === 'completed' || proc.status === 'failed' || proc.status === 'killed') {
327
+ return {
328
+ subscribed: true,
329
+ process: proc.toJSON(),
330
+ terminalsData: proc.terminalsData,
331
+ status: proc.status,
332
+ result: proc.result,
333
+ error: proc.error,
334
+ };
335
+ }
336
+
337
+ // Process is still running, client will receive future events
338
+ return {
339
+ subscribed: true,
340
+ process: proc.toJSON(),
341
+ status: proc.status,
342
+ };
343
+ }
344
+
345
+ _resetIdleTimer() {
346
+ if (!this.autoShutdown) return;
347
+
348
+ if (this._shutdownTimer) {
349
+ clearTimeout(this._shutdownTimer);
350
+ this._shutdownTimer = null;
351
+ }
352
+ }
353
+
354
+ _checkIdleShutdown() {
355
+ if (!this.autoShutdown) return;
356
+
357
+ // Don't shutdown if there are connected clients
358
+ if (this.ipc.getClientCount() > 0) return;
359
+
360
+ // Don't shutdown if there are running processes
361
+ if (this.processManager.hasRunningProcesses()) return;
362
+
363
+ // Schedule shutdown
364
+ console.log(`No clients or running processes. Shutting down in ${this.idleTimeout / 1000}s...`);
365
+ this._shutdownTimer = setTimeout(() => {
366
+ // Double-check before shutdown
367
+ if (this.ipc.getClientCount() === 0 && !this.processManager.hasRunningProcesses()) {
368
+ console.log('Idle timeout reached. Shutting down.');
369
+ this.shutdown();
370
+ }
371
+ }, this.idleTimeout);
372
+ }
373
+ }
374
+
375
+ // CLI entry point
376
+ async function main() {
377
+ const args = process.argv.slice(2);
378
+
379
+ const options = {
380
+ autoShutdown: !args.includes('--no-auto-shutdown'),
381
+ };
382
+
383
+ const daemon = new StoneDaemon(options);
384
+ await daemon.start();
385
+
386
+ // Keep process running
387
+ console.log('Stone daemon is running. Press Ctrl+C to stop.');
388
+ }
389
+
390
+ // Run if executed directly
391
+ if (process.argv[1] && process.argv[1].includes('daemon.js')) {
392
+ main().catch((err) => {
393
+ console.error('Fatal error:', err);
394
+ process.exit(1);
395
+ });
396
+ }
397
+
398
+ export default StoneDaemon;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Stone Daemon - Process management for Stone scripts
3
+ *
4
+ * Exports:
5
+ * - StoneDaemon - Main daemon class
6
+ * - IPCServer - IPC server for client connections
7
+ * - IPCClientConnection - Client for connecting to daemon
8
+ * - ProcessManager - Process table and lifecycle
9
+ * - ProcessRunner - Script execution with streaming
10
+ */
11
+
12
+ export { StoneDaemon } from './daemon.js';
13
+ export { default as StoneDaemon_default } from './daemon.js';
14
+ export { IPCServer, IPCClientConnection, getSocketPath } from './IPCServer.js';
15
+ export { ProcessManager, StoneProcess } from './ProcessManager.js';
16
+ export { ProcessRunner, runProcess, StreamingOutputAdapter } from './ProcessRunner.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Frontend Analysis - Barrel exports
3
+ */
4
+ export { LivenessAnalyzer } from './livenessAnalyzer.js';
5
+ export { ImportAnalyzer, TreeShaker, createTreeShaker } from './treeShaker.js';