2026-02-10 09:10:18 +00:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mock "Rust binary" for testing the RustBridge IPC protocol.
|
2026-02-11 00:12:56 +00:00
|
|
|
* Reads JSON lines from stdin via Buffer-based scanner, writes JSON lines to stdout.
|
2026-02-10 09:10:18 +00:00
|
|
|
* Emits a ready event on startup.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Emit ready event
|
|
|
|
|
const readyEvent = JSON.stringify({ event: 'ready', data: { version: '1.0.0' } });
|
|
|
|
|
process.stdout.write(readyEvent + '\n');
|
|
|
|
|
|
2026-02-11 00:12:56 +00:00
|
|
|
// Buffer-based newline scanner for stdin (mirrors the RustBridge approach)
|
|
|
|
|
let stdinBuffer = Buffer.alloc(0);
|
|
|
|
|
|
|
|
|
|
process.stdin.on('data', (chunk) => {
|
|
|
|
|
stdinBuffer = Buffer.concat([stdinBuffer, chunk]);
|
|
|
|
|
|
|
|
|
|
let newlineIndex;
|
|
|
|
|
while ((newlineIndex = stdinBuffer.indexOf(0x0A)) !== -1) {
|
|
|
|
|
const lineBuffer = stdinBuffer.subarray(0, newlineIndex);
|
|
|
|
|
stdinBuffer = stdinBuffer.subarray(newlineIndex + 1);
|
|
|
|
|
const line = lineBuffer.toString('utf8').trim();
|
|
|
|
|
if (line) {
|
|
|
|
|
handleLine(line);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-02-10 09:10:18 +00:00
|
|
|
|
2026-02-11 00:12:56 +00:00
|
|
|
/**
|
|
|
|
|
* Backpressure-aware write to stdout.
|
|
|
|
|
*/
|
|
|
|
|
function writeResponse(data) {
|
|
|
|
|
const json = JSON.stringify(data) + '\n';
|
|
|
|
|
if (!process.stdout.write(json)) {
|
|
|
|
|
// Wait for drain before continuing
|
|
|
|
|
process.stdout.once('drain', () => {});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleLine(line) {
|
2026-02-10 09:10:18 +00:00
|
|
|
let request;
|
|
|
|
|
try {
|
2026-02-11 00:12:56 +00:00
|
|
|
request = JSON.parse(line);
|
2026-02-10 09:10:18 +00:00
|
|
|
} catch {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { id, method, params } = request;
|
|
|
|
|
|
|
|
|
|
if (method === 'echo') {
|
|
|
|
|
// Echo back the params as result
|
2026-02-11 00:12:56 +00:00
|
|
|
writeResponse({ id, success: true, result: params });
|
|
|
|
|
} else if (method === 'largeEcho') {
|
|
|
|
|
// Echo back params (same as echo, named distinctly for large payload tests)
|
|
|
|
|
writeResponse({ id, success: true, result: params });
|
2026-02-10 09:10:18 +00:00
|
|
|
} else if (method === 'error') {
|
|
|
|
|
// Return an error
|
2026-02-11 00:12:56 +00:00
|
|
|
writeResponse({ id, success: false, error: 'Test error message' });
|
2026-02-10 09:10:18 +00:00
|
|
|
} else if (method === 'emitEvent') {
|
|
|
|
|
// Emit a custom event, then respond with success
|
2026-02-11 00:12:56 +00:00
|
|
|
writeResponse({ event: params.eventName, data: params.eventData });
|
|
|
|
|
writeResponse({ id, success: true, result: null });
|
2026-02-10 09:10:18 +00:00
|
|
|
} else if (method === 'slow') {
|
|
|
|
|
// Respond after a delay
|
|
|
|
|
setTimeout(() => {
|
2026-02-11 00:12:56 +00:00
|
|
|
writeResponse({ id, success: true, result: { delayed: true } });
|
2026-02-10 09:10:18 +00:00
|
|
|
}, 100);
|
2026-02-11 00:12:56 +00:00
|
|
|
} else if (method === 'streamEcho') {
|
|
|
|
|
// Send params.count stream chunks, then final response
|
|
|
|
|
const count = params.count || 0;
|
|
|
|
|
let sent = 0;
|
|
|
|
|
const interval = setInterval(() => {
|
|
|
|
|
if (sent < count) {
|
|
|
|
|
writeResponse({ id, stream: true, data: { index: sent, value: `chunk_${sent}` } });
|
|
|
|
|
sent++;
|
|
|
|
|
} else {
|
|
|
|
|
clearInterval(interval);
|
|
|
|
|
writeResponse({ id, success: true, result: { totalChunks: count } });
|
|
|
|
|
}
|
|
|
|
|
}, 10);
|
|
|
|
|
} else if (method === 'streamError') {
|
|
|
|
|
// Send 1 chunk, then error
|
|
|
|
|
writeResponse({ id, stream: true, data: { index: 0, value: 'before_error' } });
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
writeResponse({ id, success: false, error: 'Stream error after chunk' });
|
|
|
|
|
}, 20);
|
|
|
|
|
} else if (method === 'streamEmpty') {
|
|
|
|
|
// Zero chunks, immediate final response
|
|
|
|
|
writeResponse({ id, success: true, result: { totalChunks: 0 } });
|
2026-02-10 09:10:18 +00:00
|
|
|
} else if (method === 'exit') {
|
|
|
|
|
// Graceful exit
|
2026-02-11 00:12:56 +00:00
|
|
|
writeResponse({ id, success: true, result: null });
|
2026-02-10 09:10:18 +00:00
|
|
|
process.exit(0);
|
|
|
|
|
} else {
|
|
|
|
|
// Unknown command
|
2026-02-11 00:12:56 +00:00
|
|
|
writeResponse({ id, success: false, error: `Unknown method: ${method}` });
|
2026-02-10 09:10:18 +00:00
|
|
|
}
|
2026-02-11 00:12:56 +00:00
|
|
|
}
|
2026-02-10 09:10:18 +00:00
|
|
|
|
|
|
|
|
// Handle SIGTERM gracefully
|
|
|
|
|
process.on('SIGTERM', () => {
|
|
|
|
|
process.exit(0);
|
|
|
|
|
});
|