@forthix/forthic 0.7.0 → 0.7.2

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 (95) hide show
  1. package/dist/cjs/common/type_utils.js.map +1 -1
  2. package/dist/cjs/forthic/interpreter.d.ts +2 -1
  3. package/dist/cjs/forthic/interpreter.js +10 -0
  4. package/dist/cjs/forthic/interpreter.js.map +1 -1
  5. package/dist/cjs/forthic/literals.d.ts +0 -1
  6. package/dist/cjs/forthic/literals.js +14 -15
  7. package/dist/cjs/forthic/literals.js.map +1 -1
  8. package/dist/cjs/forthic/modules/standard/datetime_module.d.ts +0 -1
  9. package/dist/cjs/forthic/modules/standard/datetime_module.js +19 -20
  10. package/dist/cjs/forthic/modules/standard/datetime_module.js.map +1 -1
  11. package/dist/cjs/forthic/utils.d.ts +0 -1
  12. package/dist/cjs/forthic/utils.js +1 -2
  13. package/dist/cjs/forthic/utils.js.map +1 -1
  14. package/dist/cjs/grpc/serializer.js +3 -4
  15. package/dist/cjs/grpc/serializer.js.map +1 -1
  16. package/dist/cjs/grpc/server.test.d.ts +1 -0
  17. package/dist/cjs/grpc/server.test.js +156 -0
  18. package/dist/cjs/grpc/server.test.js.map +1 -0
  19. package/dist/cjs/grpc/temporal_utils.d.ts +36 -0
  20. package/dist/cjs/grpc/temporal_utils.js +80 -0
  21. package/dist/cjs/grpc/temporal_utils.js.map +1 -0
  22. package/dist/cjs/test-setup.d.ts +1 -0
  23. package/dist/cjs/test-setup.js +4 -0
  24. package/dist/cjs/test-setup.js.map +1 -0
  25. package/dist/cjs/websocket/action_cable_client.d.ts +106 -0
  26. package/dist/cjs/websocket/action_cable_client.js +269 -0
  27. package/dist/cjs/websocket/action_cable_client.js.map +1 -0
  28. package/dist/cjs/websocket/client.d.ts +103 -0
  29. package/dist/cjs/websocket/client.js +266 -0
  30. package/dist/cjs/websocket/client.js.map +1 -0
  31. package/dist/cjs/websocket/remote_module.d.ts +68 -0
  32. package/dist/cjs/websocket/remote_module.js +107 -0
  33. package/dist/cjs/websocket/remote_module.js.map +1 -0
  34. package/dist/cjs/websocket/remote_word.d.ts +53 -0
  35. package/dist/cjs/websocket/remote_word.js +93 -0
  36. package/dist/cjs/websocket/remote_word.js.map +1 -0
  37. package/dist/cjs/websocket/runtime_manager.d.ts +69 -0
  38. package/dist/cjs/websocket/runtime_manager.js +112 -0
  39. package/dist/cjs/websocket/runtime_manager.js.map +1 -0
  40. package/dist/cjs/websocket/serializer.js +3 -4
  41. package/dist/cjs/websocket/serializer.js.map +1 -1
  42. package/dist/esm/common/type_utils.js.map +1 -1
  43. package/dist/esm/forthic/interpreter.d.ts +2 -1
  44. package/dist/esm/forthic/interpreter.js +11 -1
  45. package/dist/esm/forthic/interpreter.js.map +1 -1
  46. package/dist/esm/forthic/literals.d.ts +0 -1
  47. package/dist/esm/forthic/literals.js +0 -1
  48. package/dist/esm/forthic/literals.js.map +1 -1
  49. package/dist/esm/forthic/modules/standard/datetime_module.d.ts +0 -1
  50. package/dist/esm/forthic/modules/standard/datetime_module.js +0 -1
  51. package/dist/esm/forthic/modules/standard/datetime_module.js.map +1 -1
  52. package/dist/esm/forthic/utils.d.ts +0 -1
  53. package/dist/esm/forthic/utils.js +0 -1
  54. package/dist/esm/forthic/utils.js.map +1 -1
  55. package/dist/esm/grpc/serializer.js +0 -1
  56. package/dist/esm/grpc/serializer.js.map +1 -1
  57. package/dist/esm/grpc/server.test.d.ts +1 -0
  58. package/dist/esm/grpc/server.test.js +121 -0
  59. package/dist/esm/grpc/server.test.js.map +1 -0
  60. package/dist/esm/grpc/temporal_utils.d.ts +36 -0
  61. package/dist/esm/grpc/temporal_utils.js +72 -0
  62. package/dist/esm/grpc/temporal_utils.js.map +1 -0
  63. package/dist/esm/test-setup.d.ts +1 -0
  64. package/dist/esm/test-setup.js +2 -0
  65. package/dist/esm/test-setup.js.map +1 -0
  66. package/dist/esm/websocket/action_cable_client.d.ts +106 -0
  67. package/dist/esm/websocket/action_cable_client.js +265 -0
  68. package/dist/esm/websocket/action_cable_client.js.map +1 -0
  69. package/dist/esm/websocket/client.d.ts +103 -0
  70. package/dist/esm/websocket/client.js +262 -0
  71. package/dist/esm/websocket/client.js.map +1 -0
  72. package/dist/esm/websocket/remote_module.d.ts +68 -0
  73. package/dist/esm/websocket/remote_module.js +103 -0
  74. package/dist/esm/websocket/remote_module.js.map +1 -0
  75. package/dist/esm/websocket/remote_word.d.ts +53 -0
  76. package/dist/esm/websocket/remote_word.js +89 -0
  77. package/dist/esm/websocket/remote_word.js.map +1 -0
  78. package/dist/esm/websocket/runtime_manager.d.ts +69 -0
  79. package/dist/esm/websocket/runtime_manager.js +108 -0
  80. package/dist/esm/websocket/runtime_manager.js.map +1 -0
  81. package/dist/esm/websocket/serializer.js +0 -1
  82. package/dist/esm/websocket/serializer.js.map +1 -1
  83. package/package.json +3 -2
  84. package/dist/cjs/forthic/decorators/schemaUtils.d.ts +0 -70
  85. package/dist/cjs/forthic/decorators/schemaUtils.js +0 -77
  86. package/dist/cjs/forthic/decorators/schemaUtils.js.map +0 -1
  87. package/dist/cjs/forthic/decorators/stackEffect.d.ts +0 -63
  88. package/dist/cjs/forthic/decorators/stackEffect.js +0 -179
  89. package/dist/cjs/forthic/decorators/stackEffect.js.map +0 -1
  90. package/dist/esm/forthic/decorators/schemaUtils.d.ts +0 -70
  91. package/dist/esm/forthic/decorators/schemaUtils.js +0 -72
  92. package/dist/esm/forthic/decorators/schemaUtils.js.map +0 -1
  93. package/dist/esm/forthic/decorators/stackEffect.d.ts +0 -63
  94. package/dist/esm/forthic/decorators/stackEffect.js +0 -174
  95. package/dist/esm/forthic/decorators/stackEffect.js.map +0 -1
@@ -0,0 +1,103 @@
1
+ /**
2
+ * WebSocket Client for Forthic
3
+ * Browser-compatible client that mirrors GrpcClient API
4
+ * Connects to Rails ActionCable server and executes remote words
5
+ */
6
+ import { StackValue } from './serializer.js';
7
+ export interface ModuleSummary {
8
+ name: string;
9
+ description: string;
10
+ word_count: number;
11
+ }
12
+ export interface WordInfo {
13
+ name: string;
14
+ stack_effect: string;
15
+ description: string;
16
+ }
17
+ export interface ModuleInfo {
18
+ module_name: string;
19
+ words: WordInfo[];
20
+ }
21
+ export interface ProgressUpdate {
22
+ step: number;
23
+ total_steps: number;
24
+ current_word: string;
25
+ message?: string;
26
+ partial_stack?: StackValue[];
27
+ }
28
+ export interface WebSocketClientConfig {
29
+ url: string;
30
+ timezone?: string;
31
+ reconnect?: boolean;
32
+ reconnectDelay?: number;
33
+ channel?: string;
34
+ }
35
+ /**
36
+ * WebSocket client for executing words in remote Forthic runtimes
37
+ * Mirrors the GrpcClient API for compatibility
38
+ */
39
+ export declare class WebSocketClient {
40
+ private ws?;
41
+ private config;
42
+ private pendingRequests;
43
+ private messageId;
44
+ private connected;
45
+ private connectionPromise?;
46
+ constructor(config: WebSocketClientConfig);
47
+ /**
48
+ * Connect to the WebSocket server
49
+ */
50
+ private connect;
51
+ /**
52
+ * Handle incoming WebSocket messages
53
+ */
54
+ private handleMessage;
55
+ /**
56
+ * Generate next message ID
57
+ */
58
+ private nextMessageId;
59
+ /**
60
+ * Send a request and wait for response
61
+ */
62
+ private sendRequest;
63
+ /**
64
+ * Send a streaming request with progress callbacks
65
+ */
66
+ private sendStreamingRequest;
67
+ /**
68
+ * Ensure WebSocket is connected
69
+ */
70
+ private ensureConnected;
71
+ /**
72
+ * Execute a word in the remote runtime
73
+ * Mirrors GrpcClient.executeWord
74
+ */
75
+ executeWord(word: string, stack: any[]): Promise<any[]>;
76
+ /**
77
+ * Execute a sequence of words (batched execution)
78
+ * Mirrors GrpcClient.executeSequence
79
+ */
80
+ executeSequence(words: string[], stack: any[]): Promise<any[]>;
81
+ /**
82
+ * List available runtime-specific modules
83
+ * Mirrors GrpcClient.listModules
84
+ */
85
+ listModules(): Promise<ModuleSummary[]>;
86
+ /**
87
+ * Get detailed information about a specific module
88
+ * Mirrors GrpcClient.getModuleInfo
89
+ */
90
+ getModuleInfo(moduleName: string): Promise<ModuleInfo>;
91
+ /**
92
+ * Execute code with streaming progress updates (NEW capability)
93
+ */
94
+ streamingExecute(code: string, stack: any[], onProgress?: (progress: ProgressUpdate) => void): Promise<any[]>;
95
+ /**
96
+ * Close the WebSocket connection
97
+ */
98
+ close(): void;
99
+ /**
100
+ * Check if the client is connected
101
+ */
102
+ isConnected(): boolean;
103
+ }
@@ -0,0 +1,262 @@
1
+ /**
2
+ * WebSocket Client for Forthic
3
+ * Browser-compatible client that mirrors GrpcClient API
4
+ * Connects to Rails ActionCable server and executes remote words
5
+ */
6
+ import { serializeStack, deserializeStack } from './serializer.js';
7
+ import { RemoteRuntimeError } from './errors.js';
8
+ /**
9
+ * WebSocket client for executing words in remote Forthic runtimes
10
+ * Mirrors the GrpcClient API for compatibility
11
+ */
12
+ export class WebSocketClient {
13
+ ws;
14
+ config;
15
+ pendingRequests = new Map();
16
+ messageId = 0;
17
+ connected = false;
18
+ connectionPromise;
19
+ constructor(config) {
20
+ this.config = {
21
+ url: config.url,
22
+ timezone: config.timezone || 'UTC',
23
+ reconnect: config.reconnect ?? true,
24
+ reconnectDelay: config.reconnectDelay || 3000,
25
+ channel: config.channel || 'ForthicRuntimeChannel',
26
+ };
27
+ this.connect();
28
+ }
29
+ /**
30
+ * Connect to the WebSocket server
31
+ */
32
+ connect() {
33
+ if (this.connectionPromise) {
34
+ return this.connectionPromise;
35
+ }
36
+ this.connectionPromise = new Promise((resolve, reject) => {
37
+ try {
38
+ this.ws = new WebSocket(this.config.url);
39
+ this.ws.onopen = () => {
40
+ this.connected = true;
41
+ // Subscribe to ActionCable channel
42
+ const subscribeMessage = {
43
+ command: 'subscribe',
44
+ identifier: JSON.stringify({
45
+ channel: this.config.channel,
46
+ timezone: this.config.timezone,
47
+ }),
48
+ };
49
+ this.ws.send(JSON.stringify(subscribeMessage));
50
+ resolve();
51
+ };
52
+ this.ws.onmessage = (event) => {
53
+ this.handleMessage(event);
54
+ };
55
+ this.ws.onerror = (error) => {
56
+ console.error('WebSocket error:', error);
57
+ reject(new Error('WebSocket connection error'));
58
+ };
59
+ this.ws.onclose = () => {
60
+ this.connected = false;
61
+ this.connectionPromise = undefined;
62
+ // Reject all pending requests
63
+ for (const [id, pending] of this.pendingRequests.entries()) {
64
+ pending.reject(new Error('WebSocket connection closed'));
65
+ }
66
+ this.pendingRequests.clear();
67
+ // Attempt reconnect if enabled
68
+ if (this.config.reconnect) {
69
+ setTimeout(() => this.connect(), this.config.reconnectDelay);
70
+ }
71
+ };
72
+ }
73
+ catch (error) {
74
+ reject(error);
75
+ }
76
+ });
77
+ return this.connectionPromise;
78
+ }
79
+ /**
80
+ * Handle incoming WebSocket messages
81
+ */
82
+ handleMessage(event) {
83
+ try {
84
+ const data = JSON.parse(event.data);
85
+ // Handle ActionCable control messages
86
+ if (data.type === 'welcome' || data.type === 'ping' || data.type === 'confirm_subscription') {
87
+ return;
88
+ }
89
+ // Extract the actual message from ActionCable envelope
90
+ const message = data.message || data;
91
+ if (!message.id) {
92
+ console.warn('Received message without ID:', message);
93
+ return;
94
+ }
95
+ // Handle streaming progress updates
96
+ if (message.type === 'streaming_progress') {
97
+ const pending = this.pendingRequests.get(message.id);
98
+ if (pending?.onProgress) {
99
+ pending.onProgress(message.progress);
100
+ }
101
+ return;
102
+ }
103
+ // Handle final responses
104
+ const pending = this.pendingRequests.get(message.id);
105
+ if (!pending) {
106
+ console.warn('Received response for unknown request:', message.id);
107
+ return;
108
+ }
109
+ if (message.error) {
110
+ pending.reject(new RemoteRuntimeError(message.error));
111
+ }
112
+ else {
113
+ pending.resolve(message.result);
114
+ }
115
+ this.pendingRequests.delete(message.id);
116
+ }
117
+ catch (error) {
118
+ console.error('Error handling WebSocket message:', error);
119
+ }
120
+ }
121
+ /**
122
+ * Generate next message ID
123
+ */
124
+ nextMessageId() {
125
+ return `msg-${++this.messageId}-${Date.now()}`;
126
+ }
127
+ /**
128
+ * Send a request and wait for response
129
+ */
130
+ async sendRequest(message) {
131
+ await this.ensureConnected();
132
+ return new Promise((resolve, reject) => {
133
+ this.pendingRequests.set(message.id, { resolve, reject });
134
+ // Wrap message in ActionCable format
135
+ const actionCableMessage = {
136
+ command: 'message',
137
+ identifier: JSON.stringify({
138
+ channel: this.config.channel,
139
+ }),
140
+ data: JSON.stringify(message),
141
+ };
142
+ this.ws.send(JSON.stringify(actionCableMessage));
143
+ });
144
+ }
145
+ /**
146
+ * Send a streaming request with progress callbacks
147
+ */
148
+ async sendStreamingRequest(message, onProgress) {
149
+ await this.ensureConnected();
150
+ return new Promise((resolve, reject) => {
151
+ this.pendingRequests.set(message.id, { resolve, reject, onProgress });
152
+ // Wrap message in ActionCable format
153
+ const actionCableMessage = {
154
+ command: 'message',
155
+ identifier: JSON.stringify({
156
+ channel: this.config.channel,
157
+ }),
158
+ data: JSON.stringify(message),
159
+ };
160
+ this.ws.send(JSON.stringify(actionCableMessage));
161
+ });
162
+ }
163
+ /**
164
+ * Ensure WebSocket is connected
165
+ */
166
+ async ensureConnected() {
167
+ if (!this.connected) {
168
+ await this.connect();
169
+ }
170
+ }
171
+ /**
172
+ * Execute a word in the remote runtime
173
+ * Mirrors GrpcClient.executeWord
174
+ */
175
+ async executeWord(word, stack) {
176
+ const message = {
177
+ type: 'execute_word',
178
+ id: this.nextMessageId(),
179
+ params: {
180
+ word,
181
+ stack: serializeStack(stack),
182
+ },
183
+ };
184
+ const result = await this.sendRequest(message);
185
+ return deserializeStack(result.stack);
186
+ }
187
+ /**
188
+ * Execute a sequence of words (batched execution)
189
+ * Mirrors GrpcClient.executeSequence
190
+ */
191
+ async executeSequence(words, stack) {
192
+ const message = {
193
+ type: 'execute_sequence',
194
+ id: this.nextMessageId(),
195
+ params: {
196
+ words,
197
+ stack: serializeStack(stack),
198
+ },
199
+ };
200
+ const result = await this.sendRequest(message);
201
+ return deserializeStack(result.stack);
202
+ }
203
+ /**
204
+ * List available runtime-specific modules
205
+ * Mirrors GrpcClient.listModules
206
+ */
207
+ async listModules() {
208
+ const message = {
209
+ type: 'list_modules',
210
+ id: this.nextMessageId(),
211
+ };
212
+ const result = await this.sendRequest(message);
213
+ return result.modules || [];
214
+ }
215
+ /**
216
+ * Get detailed information about a specific module
217
+ * Mirrors GrpcClient.getModuleInfo
218
+ */
219
+ async getModuleInfo(moduleName) {
220
+ const message = {
221
+ type: 'get_module_info',
222
+ id: this.nextMessageId(),
223
+ params: {
224
+ module_name: moduleName,
225
+ },
226
+ };
227
+ return await this.sendRequest(message);
228
+ }
229
+ /**
230
+ * Execute code with streaming progress updates (NEW capability)
231
+ */
232
+ async streamingExecute(code, stack, onProgress) {
233
+ const message = {
234
+ type: 'streaming_execute',
235
+ id: this.nextMessageId(),
236
+ params: {
237
+ code,
238
+ stack: serializeStack(stack),
239
+ },
240
+ };
241
+ const result = await this.sendStreamingRequest(message, onProgress);
242
+ return deserializeStack(result.stack);
243
+ }
244
+ /**
245
+ * Close the WebSocket connection
246
+ */
247
+ close() {
248
+ if (this.ws) {
249
+ this.config.reconnect = false; // Disable auto-reconnect
250
+ this.ws.close();
251
+ this.ws = undefined;
252
+ this.connected = false;
253
+ }
254
+ }
255
+ /**
256
+ * Check if the client is connected
257
+ */
258
+ isConnected() {
259
+ return this.connected;
260
+ }
261
+ }
262
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/websocket/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * WebSocketRemoteModule - Module that wraps runtime-specific words from a remote runtime
3
+ * Mirrors RemoteModule from gRPC implementation
4
+ */
5
+ import { Module } from '../forthic/module.js';
6
+ import { Interpreter } from '../forthic/interpreter.js';
7
+ import { ActionCableClient, type ModuleInfo } from './action_cable_client.js';
8
+ /**
9
+ * WebSocketRemoteModule - Module containing proxy words that execute in a remote runtime
10
+ *
11
+ * This module discovers words from a remote runtime (e.g., pandas module in Ruby)
12
+ * and creates WebSocketRemoteWord proxies for each discovered word. When used in TypeScript
13
+ * Forthic code, these words execute in the remote runtime via ActionCable.
14
+ *
15
+ * Example usage:
16
+ * ```typescript
17
+ * const client = new ActionCableClient({ url: 'ws://localhost:3000/cable' });
18
+ * const pandasModule = new WebSocketRemoteModule('pandas', client, 'rails');
19
+ * await pandasModule.initialize();
20
+ * interp.register_module(pandasModule);
21
+ * interp.use_modules(['pandas']);
22
+ *
23
+ * // Now pandas words execute in Rails runtime
24
+ * await interp.run(`
25
+ * [ [[.name "Alice"] [.age 30]] REC]
26
+ * DF-FROM-RECORDS # Executes in Rails!
27
+ * `);
28
+ * ```
29
+ */
30
+ export declare class WebSocketRemoteModule extends Module {
31
+ private client;
32
+ private runtimeName;
33
+ private initialized;
34
+ private moduleInfo;
35
+ /**
36
+ * @param moduleName - Name of the module in the remote runtime (e.g., "pandas")
37
+ * @param client - ActionCable client connected to the remote runtime
38
+ * @param runtimeName - Name of the runtime (e.g., "rails") for debugging
39
+ */
40
+ constructor(moduleName: string, client: ActionCableClient, runtimeName?: string);
41
+ /**
42
+ * Initialize the module by discovering words from the remote runtime
43
+ *
44
+ * This must be called before the module is registered with an interpreter.
45
+ * It fetches the module metadata and creates WebSocketRemoteWord proxies for each word.
46
+ */
47
+ initialize(): Promise<void>;
48
+ /**
49
+ * Override set_interp to ensure module is initialized
50
+ */
51
+ set_interp(interp: Interpreter): void;
52
+ /**
53
+ * Get the module metadata from the remote runtime
54
+ */
55
+ getModuleInfo(): ModuleInfo | null;
56
+ /**
57
+ * Get runtime name for debugging
58
+ */
59
+ getRuntimeName(): string;
60
+ /**
61
+ * Check if module is initialized
62
+ */
63
+ isInitialized(): boolean;
64
+ /**
65
+ * Get count of discovered words
66
+ */
67
+ getWordCount(): number;
68
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * WebSocketRemoteModule - Module that wraps runtime-specific words from a remote runtime
3
+ * Mirrors RemoteModule from gRPC implementation
4
+ */
5
+ import { Module } from '../forthic/module.js';
6
+ import { WebSocketRemoteWord } from './remote_word.js';
7
+ /**
8
+ * WebSocketRemoteModule - Module containing proxy words that execute in a remote runtime
9
+ *
10
+ * This module discovers words from a remote runtime (e.g., pandas module in Ruby)
11
+ * and creates WebSocketRemoteWord proxies for each discovered word. When used in TypeScript
12
+ * Forthic code, these words execute in the remote runtime via ActionCable.
13
+ *
14
+ * Example usage:
15
+ * ```typescript
16
+ * const client = new ActionCableClient({ url: 'ws://localhost:3000/cable' });
17
+ * const pandasModule = new WebSocketRemoteModule('pandas', client, 'rails');
18
+ * await pandasModule.initialize();
19
+ * interp.register_module(pandasModule);
20
+ * interp.use_modules(['pandas']);
21
+ *
22
+ * // Now pandas words execute in Rails runtime
23
+ * await interp.run(`
24
+ * [ [[.name "Alice"] [.age 30]] REC]
25
+ * DF-FROM-RECORDS # Executes in Rails!
26
+ * `);
27
+ * ```
28
+ */
29
+ export class WebSocketRemoteModule extends Module {
30
+ client;
31
+ runtimeName;
32
+ initialized = false;
33
+ moduleInfo = null;
34
+ /**
35
+ * @param moduleName - Name of the module in the remote runtime (e.g., "pandas")
36
+ * @param client - ActionCable client connected to the remote runtime
37
+ * @param runtimeName - Name of the runtime (e.g., "rails") for debugging
38
+ */
39
+ constructor(moduleName, client, runtimeName = 'remote') {
40
+ super(moduleName);
41
+ this.client = client;
42
+ this.runtimeName = runtimeName;
43
+ }
44
+ /**
45
+ * Initialize the module by discovering words from the remote runtime
46
+ *
47
+ * This must be called before the module is registered with an interpreter.
48
+ * It fetches the module metadata and creates WebSocketRemoteWord proxies for each word.
49
+ */
50
+ async initialize() {
51
+ if (this.initialized) {
52
+ return;
53
+ }
54
+ try {
55
+ // Discover module info from remote runtime
56
+ this.moduleInfo = await this.client.getModuleInfo(this.name);
57
+ // Create WebSocketRemoteWord for each discovered word
58
+ for (const wordInfo of this.moduleInfo.words) {
59
+ const remoteWord = new WebSocketRemoteWord(wordInfo.name, this.client, this.runtimeName, this.name, wordInfo.stack_effect, wordInfo.description);
60
+ // Add as exportable word (visible when module is imported)
61
+ this.add_exportable_word(remoteWord);
62
+ }
63
+ this.initialized = true;
64
+ }
65
+ catch (error) {
66
+ throw new Error(`Failed to initialize remote module '${this.name}' from ${this.runtimeName} runtime: ${error.message}`);
67
+ }
68
+ }
69
+ /**
70
+ * Override set_interp to ensure module is initialized
71
+ */
72
+ set_interp(interp) {
73
+ if (!this.initialized) {
74
+ throw new Error(`WebSocketRemoteModule '${this.name}' must be initialized before being registered with an interpreter. Call await module.initialize() first.`);
75
+ }
76
+ super.set_interp(interp);
77
+ }
78
+ /**
79
+ * Get the module metadata from the remote runtime
80
+ */
81
+ getModuleInfo() {
82
+ return this.moduleInfo;
83
+ }
84
+ /**
85
+ * Get runtime name for debugging
86
+ */
87
+ getRuntimeName() {
88
+ return this.runtimeName;
89
+ }
90
+ /**
91
+ * Check if module is initialized
92
+ */
93
+ isInitialized() {
94
+ return this.initialized;
95
+ }
96
+ /**
97
+ * Get count of discovered words
98
+ */
99
+ getWordCount() {
100
+ return this.moduleInfo?.words?.length || 0;
101
+ }
102
+ }
103
+ //# sourceMappingURL=remote_module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote_module.js","sourceRoot":"","sources":["../../../src/websocket/remote_module.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAG9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,qBAAsB,SAAQ,MAAM;IACvC,MAAM,CAAoB;IAC1B,WAAW,CAAS;IACpB,WAAW,GAAY,KAAK,CAAC;IAC7B,UAAU,GAAsB,IAAI,CAAC;IAE7C;;;;OAIG;IACH,YAAY,UAAkB,EAAE,MAAyB,EAAE,cAAsB,QAAQ;QACvF,KAAK,CAAC,UAAU,CAAC,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,2CAA2C;YAC3C,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE7D,sDAAsD;YACtD,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBAC7C,MAAM,UAAU,GAAG,IAAI,mBAAmB,CACxC,QAAQ,CAAC,IAAI,EACb,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,IAAI,EACT,QAAQ,CAAC,YAAY,EACrB,QAAQ,CAAC,WAAW,CACrB,CAAC;gBAEF,2DAA2D;gBAC3D,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;YACvC,CAAC;YAED,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,uCAAuC,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC,WAAW,aAAc,KAAe,CAAC,OAAO,EAAE,CAClH,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,MAAmB;QAC5B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CACb,0BAA0B,IAAI,CAAC,IAAI,0GAA0G,CAC9I,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC;IAC7C,CAAC;CACF"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * WebSocketRemoteWord - Word that executes in a remote runtime via ActionCable
3
+ * Mirrors RemoteWord from gRPC implementation
4
+ */
5
+ import { Word, RuntimeInfo } from '../forthic/module.js';
6
+ import { Interpreter } from '../forthic/interpreter.js';
7
+ import { ActionCableClient } from './action_cable_client.js';
8
+ /**
9
+ * WebSocketRemoteWord - Proxy word that delegates execution to a remote runtime
10
+ *
11
+ * When executed:
12
+ * 1. Captures current interpreter stack
13
+ * 2. Sends word name + stack to remote runtime via ActionCable
14
+ * 3. Replaces local stack with result stack from remote execution
15
+ *
16
+ * This allows seamless integration of remote runtime words
17
+ * into the local TypeScript interpreter.
18
+ */
19
+ export declare class WebSocketRemoteWord extends Word {
20
+ private client;
21
+ private runtimeName;
22
+ private moduleName;
23
+ stackEffect: string;
24
+ description: string;
25
+ /**
26
+ * @param name - Word name (e.g., "DF_FROM_RECORDS")
27
+ * @param client - ActionCable client connected to remote runtime
28
+ * @param runtimeName - Name of remote runtime (e.g., "rails", "ruby")
29
+ * @param moduleName - Module name (e.g., "pandas")
30
+ * @param stackEffect - Stack notation (e.g., "( records:array -- df:DataFrame )")
31
+ * @param description - Human-readable description
32
+ */
33
+ constructor(name: string, client: ActionCableClient, runtimeName: string, moduleName: string, stackEffect?: string, description?: string);
34
+ /**
35
+ * Execute word in remote runtime
36
+ *
37
+ * Captures entire stack, sends to remote runtime, and replaces stack with result.
38
+ */
39
+ execute(interp: Interpreter): Promise<void>;
40
+ /**
41
+ * Get runtime name for debugging/introspection
42
+ */
43
+ getRuntimeName(): string;
44
+ /**
45
+ * Get module name for debugging/introspection
46
+ */
47
+ getModuleName(): string;
48
+ /**
49
+ * Get runtime execution information
50
+ * RemoteWords are runtime-specific and can only execute in their designated runtime
51
+ */
52
+ getRuntimeInfo(): RuntimeInfo;
53
+ }