#!/usr/bin/env node
/**
* conductor-mock-server.js
*
* A minimal HTTP mock server for local Conductor development and validation.
* It echoes request bodies through routes that mimic the public HTTPBin API,
* so Conductor project examples can run fully offline or from within Docker.
*
* Usage:
* node examples/mock-server/conductor-mock-server.js
*
* Listens on:
* http://localhost:8787 (host access)
* http://host.docker.internal:8787 (Docker container access on Mac/Win)
*
* Routes:
* POST /anything — parse JSON body, return { json:
, method, url, headers }
* GET /get — return { args: , method, url, headers }
* * — 404 JSON error
*/
'use strict';
const http = require('http');
const url = require('url');
const PORT = 8787;
const HOST = '0.0.0.0';
// ── Request-body reader ───────────────────────────────────────────────────────
function readBody(req) {
return new Promise((resolve, reject) => {
const chunks = [];
req.on('data', (chunk) => chunks.push(chunk));
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
req.on('error', reject);
});
}
// ── Response helpers ──────────────────────────────────────────────────────────
function sendJson(res, status, body) {
const payload = JSON.stringify(body, null, 2);
res.writeHead(status, {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload),
'Access-Control-Allow-Origin': '*',
});
res.end(payload);
}
// ── Route handlers ────────────────────────────────────────────────────────────
async function handleAnything(req, res) {
// Parse body (if any)
let parsedJson = null;
const rawBody = await readBody(req);
if (rawBody.trim()) {
try {
parsedJson = JSON.parse(rawBody);
} catch (e) {
sendJson(res, 400, {
error: 'invalid_json',
message: `Request body is not valid JSON: ${e.message}`,
});
return;
}
}
const parsed = url.parse(req.url, true);
sendJson(res, 200, {
method: req.method,
url: req.url,
args: parsed.query,
headers: req.headers,
json: parsedJson,
data: rawBody || '',
});
}
function handleGet(req, res) {
const parsed = url.parse(req.url, true);
sendJson(res, 200, {
method: req.method,
url: req.url,
args: parsed.query,
headers: req.headers,
});
}
// ── Server ────────────────────────────────────────────────────────────────────
const server = http.createServer(async (req, res) => {
// Strip query string for routing
const pathname = url.parse(req.url).pathname;
// OPTIONS preflight (CORS)
if (req.method === 'OPTIONS') {
res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*' });
res.end();
return;
}
try {
if (pathname === '/anything') {
await handleAnything(req, res);
} else if (pathname === '/get' && req.method === 'GET') {
handleGet(req, res);
} else {
sendJson(res, 404, {
error: 'not_found',
message: `No mock route for ${req.method} ${pathname}. ` +
`Available: POST /anything, GET /get`,
});
}
} catch (err) {
sendJson(res, 500, {
error: 'internal_error',
message: err instanceof Error ? err.message : String(err),
});
}
});
server.listen(PORT, HOST, () => {
console.log(`Conductor mock server listening on http://${HOST}:${PORT}`);
console.log(' POST /anything — echoes JSON body as { json: }');
console.log(' GET /get — echoes query parameters as { args: }');
console.log('Press Ctrl+C to stop.');
});