brainy/.recovery-workspace/dist-backup-20250910-141917/distributed/httpTransport.js
David Snelling 8ff382ca3b chore: recovery checkpoint - v3.0 API successfully recovered
CRITICAL CHECKPOINT - DO NOT PUSH TO GITHUB

Recovery Status:
- Successfully recovered brainy.ts from compiled JavaScript
- All core v3.0 API methods functional (add, get, update, delete, relate, find, etc.)
- Neural subsystem intact (562KB embedded patterns, NLP working)
- Augmentation pipeline operational (20+ augmentations)
- HNSW clustering system complete
- Triple Intelligence compiled (needs constructor fix)
- Test suite validates functionality

Changes preserved:
- 898 files with changes from last 3 days
- 144,475 insertions
- All augmentation improvements
- All test coverage enhancements
- Complete v3.0 feature set

This is a LOCAL checkpoint only - contains recovered work after corruption incident.
Created backup in .backups/brainy-full-20250910-151314.tar.gz

Branch: recovery-checkpoint-20250910-151433
Date: Wed Sep 10 03:18:04 PM PDT 2025
2025-09-10 15:18:04 -07:00

446 lines
No EOL
14 KiB
JavaScript

/**
* HTTP + SSE Transport for Zero-Config Distributed Brainy
* Simple, reliable, works everywhere - no WebSocket complexity!
* REAL PRODUCTION CODE - Handles millions of operations
*/
import * as http from 'http';
import * as https from 'https';
import { EventEmitter } from 'events';
import * as net from 'net';
import { URL } from 'url';
export class HTTPTransport extends EventEmitter {
constructor(nodeId) {
super();
this.server = null;
this.port = 0;
this.endpoints = new Map(); // nodeId -> endpoint
this.pendingRequests = new Map();
this.sseClients = new Map();
this.messageHandlers = new Map();
this.isRunning = false;
this.REQUEST_TIMEOUT = 30000; // 30 seconds
this.SSE_HEARTBEAT_INTERVAL = 15000; // 15 seconds
this.sseHeartbeatTimer = null;
this.nodeId = nodeId;
}
/**
* Start HTTP server with automatic port selection
*/
async start() {
if (this.isRunning)
return this.port;
// Create HTTP server with all handlers
this.server = http.createServer(async (req, res) => {
// Enable CORS for browser compatibility
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
res.writeHead(204);
res.end();
return;
}
const url = new URL(req.url || '/', `http://${req.headers.host}`);
try {
// Route requests
if (url.pathname === '/health') {
await this.handleHealth(req, res);
}
else if (url.pathname === '/rpc') {
await this.handleRPC(req, res);
}
else if (url.pathname === '/events') {
await this.handleSSE(req, res);
}
else if (url.pathname.startsWith('/stream/')) {
await this.handleStream(req, res, url);
}
else {
res.writeHead(404);
res.end('Not Found');
}
}
catch (err) {
console.error(`[${this.nodeId}] Request error:`, err);
res.writeHead(500);
res.end('Internal Server Error');
}
});
// Find available port
this.port = await this.findAvailablePort();
// Start server
await new Promise((resolve, reject) => {
this.server.listen(this.port, () => {
console.log(`[${this.nodeId}] HTTP transport listening on port ${this.port}`);
resolve();
});
this.server.on('error', reject);
});
this.isRunning = true;
// Start SSE heartbeat
this.startSSEHeartbeat();
this.emit('started', this.port);
return this.port;
}
/**
* Stop HTTP server
*/
async stop() {
if (!this.isRunning)
return;
this.isRunning = false;
// Stop SSE heartbeat
if (this.sseHeartbeatTimer) {
clearInterval(this.sseHeartbeatTimer);
this.sseHeartbeatTimer = null;
}
// Close all SSE connections
for (const client of this.sseClients.values()) {
client.response.end();
}
this.sseClients.clear();
// Cancel pending requests
for (const pending of this.pendingRequests.values()) {
clearTimeout(pending.timeout);
pending.reject(new Error('Transport shutting down'));
}
this.pendingRequests.clear();
// Close server
if (this.server) {
await new Promise(resolve => {
this.server.close(() => resolve());
});
this.server = null;
}
this.emit('stopped');
}
/**
* Register a node endpoint
*/
registerEndpoint(nodeId, endpoint) {
this.endpoints.set(nodeId, endpoint);
this.emit('endpointRegistered', { nodeId, endpoint });
}
/**
* Register RPC method handler
*/
registerHandler(method, handler) {
this.messageHandlers.set(method, handler);
}
/**
* Call RPC method on remote node
*/
async call(nodeId, method, params) {
const endpoint = this.endpoints.get(nodeId);
if (!endpoint) {
throw new Error(`No endpoint registered for node ${nodeId}`);
}
const message = {
id: this.generateId(),
method,
params,
timestamp: Date.now(),
from: this.nodeId,
to: nodeId
};
// Send HTTP request
const response = await this.sendHTTPRequest(endpoint, '/rpc', message);
if (response.error) {
throw new Error(response.error.message);
}
return response.result;
}
/**
* Broadcast to all SSE clients
*/
broadcast(event, data) {
const message = JSON.stringify({ event, data, timestamp: Date.now() });
for (const [clientId, client] of this.sseClients.entries()) {
try {
client.response.write(`data: ${message}\n\n`);
}
catch (err) {
// Client disconnected
console.debug(`[${this.nodeId}] SSE client ${clientId} disconnected`);
this.sseClients.delete(clientId);
}
}
}
/**
* Handle health check
*/
async handleHealth(req, res) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
status: 'healthy',
nodeId: this.nodeId,
uptime: process.uptime(),
memory: process.memoryUsage(),
connections: this.sseClients.size
}));
}
/**
* Handle RPC requests
*/
async handleRPC(req, res) {
if (req.method !== 'POST') {
res.writeHead(405);
res.end('Method Not Allowed');
return;
}
// Read body
const body = await this.readBody(req);
let message;
try {
message = JSON.parse(body);
}
catch (err) {
res.writeHead(400);
res.end('Invalid JSON');
return;
}
// Handle the RPC call
const response = {
id: message.id,
timestamp: Date.now()
};
try {
const handler = this.messageHandlers.get(message.method);
if (!handler) {
throw new Error(`Method ${message.method} not found`);
}
response.result = await handler(message.params, message.from);
}
catch (err) {
response.error = {
code: -32603,
message: err.message,
data: err.stack
};
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(response));
}
/**
* Handle SSE connections for real-time updates
*/
async handleSSE(req, res) {
// Setup SSE headers
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no' // Disable Nginx buffering
});
// Send initial connection event
const clientId = this.generateId();
res.write(`data: ${JSON.stringify({
event: 'connected',
clientId,
nodeId: this.nodeId
})}\n\n`);
// Store client
this.sseClients.set(clientId, {
id: clientId,
response: res,
lastPing: Date.now()
});
// Handle client disconnect
req.on('close', () => {
this.sseClients.delete(clientId);
this.emit('sseDisconnected', clientId);
});
this.emit('sseConnected', clientId);
}
/**
* Handle streaming data (for shard migration)
*/
async handleStream(req, res, url) {
const streamId = url.pathname.split('/').pop();
if (req.method === 'POST') {
// Receiving stream
await this.handleStreamUpload(req, res, streamId);
}
else if (req.method === 'GET') {
// Sending stream
await this.handleStreamDownload(req, res, streamId);
}
else {
res.writeHead(405);
res.end('Method Not Allowed');
}
}
/**
* Handle stream upload (receiving data)
*/
async handleStreamUpload(req, res, streamId) {
const chunks = [];
let totalSize = 0;
req.on('data', (chunk) => {
chunks.push(chunk);
totalSize += chunk.length;
// Emit progress
this.emit('streamProgress', {
streamId,
type: 'upload',
bytes: totalSize
});
});
req.on('end', () => {
const data = Buffer.concat(chunks);
// Process the streamed data
this.emit('streamComplete', {
streamId,
type: 'upload',
data,
size: totalSize
});
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: true,
streamId,
size: totalSize
}));
});
req.on('error', (err) => {
console.error(`[${this.nodeId}] Stream upload error:`, err);
res.writeHead(500);
res.end('Stream Error');
});
}
/**
* Handle stream download (sending data)
*/
async handleStreamDownload(req, res, streamId) {
// Stream download not yet implemented
// Return error response instead of fake data
res.writeHead(501, {
'Content-Type': 'application/json'
});
res.end(JSON.stringify({
error: 'Stream download not implemented',
message: 'This feature is not yet available in the current version',
streamId
}));
this.emit('streamError', {
streamId,
type: 'download',
error: 'Not implemented'
});
}
/**
* Send HTTP request to another node
*/
async sendHTTPRequest(endpoint, path, data) {
return new Promise((resolve, reject) => {
const url = new URL(path, endpoint);
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const proto = url.protocol === 'https:' ? https : http;
const req = proto.request(url, options, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
try {
const response = JSON.parse(body);
resolve(response);
}
catch (err) {
reject(new Error(`Invalid response: ${body}`));
}
});
});
req.on('error', reject);
req.on('timeout', () => {
req.destroy();
reject(new Error('Request timeout'));
});
req.setTimeout(this.REQUEST_TIMEOUT);
req.write(JSON.stringify(data));
req.end();
});
}
/**
* Read request body
*/
readBody(req) {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => resolve(body));
req.on('error', reject);
});
}
/**
* Find an available port
*/
async findAvailablePort(startPort = 7947) {
const checkPort = (port) => {
return new Promise(resolve => {
const server = net.createServer();
server.once('error', () => resolve(false));
server.once('listening', () => {
server.close();
resolve(true);
});
server.listen(port);
});
};
// Try preferred port first
if (await checkPort(startPort)) {
return startPort;
}
// Find random available port
return new Promise((resolve, reject) => {
const server = net.createServer();
server.once('error', reject);
server.once('listening', () => {
const port = server.address().port;
server.close(() => resolve(port));
});
server.listen(0); // 0 = random available port
});
}
/**
* Start SSE heartbeat to keep connections alive
*/
startSSEHeartbeat() {
this.sseHeartbeatTimer = setInterval(() => {
const now = Date.now();
const ping = JSON.stringify({ event: 'ping', timestamp: now });
for (const [clientId, client] of this.sseClients.entries()) {
try {
client.response.write(`: ping\n\n`); // SSE comment for keep-alive
client.lastPing = now;
}
catch (err) {
// Client disconnected
this.sseClients.delete(clientId);
}
}
}, this.SSE_HEARTBEAT_INTERVAL);
}
/**
* Generate unique ID
*/
generateId() {
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
}
/**
* Get connected nodes count
*/
getConnectionCount() {
return this.endpoints.size;
}
/**
* Get SSE client count
*/
getSSEClientCount() {
return this.sseClients.size;
}
}
//# sourceMappingURL=httpTransport.js.map