pub const NODE_HTTP_JS: &str = r#"
import EventEmitter from "node:events";
// ─── STATUS_CODES ────────────────────────────────────────────────────────────
const STATUS_CODES = {
200: 'OK', 201: 'Created', 204: 'No Content',
301: 'Moved Permanently', 302: 'Found', 304: 'Not Modified',
400: 'Bad Request', 401: 'Unauthorized', 403: 'Forbidden',
404: 'Not Found', 405: 'Method Not Allowed', 408: 'Request Timeout',
500: 'Internal Server Error', 502: 'Bad Gateway', 503: 'Service Unavailable',
};
const METHODS = [
'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT',
'OPTIONS', 'TRACE', 'PATCH',
];
// ─── IncomingMessage ─────────────────────────────────────────────────────────
class IncomingMessage extends EventEmitter {
constructor(statusCode, headers, body) {
super();
this.statusCode = statusCode;
this.statusMessage = STATUS_CODES[statusCode] || 'Unknown';
this.headers = headers || {};
this._body = body || '';
this.complete = false;
this.httpVersion = '1.1';
this.method = null;
this.url = '';
}
_deliver() {
if (this._body && this._body.length > 0) {
this.emit('data', this._body);
}
this.complete = true;
this.emit('end');
}
setEncoding(_encoding) { return this; }
resume() { return this; }
pause() { return this; }
destroy() { this.emit('close'); }
}
// ─── ClientRequest ───────────────────────────────────────────────────────────
class ClientRequest extends EventEmitter {
constructor(options, callback) {
super();
this._options = options;
this._body = [];
this._ended = false;
this._aborted = false;
this.socket = { remoteAddress: '127.0.0.1', remotePort: 0 };
this.method = options.method || 'GET';
this.path = options.path || '/';
if (typeof callback === 'function') {
this.once('response', callback);
}
}
write(chunk) {
if (!this._ended && !this._aborted) {
this._body.push(typeof chunk === 'string' ? chunk : String(chunk));
}
return true;
}
end(chunk, _encoding, callback) {
if (typeof chunk === 'function') { callback = chunk; chunk = undefined; }
if (typeof _encoding === 'function') { callback = _encoding; }
if (chunk) this.write(chunk);
if (typeof callback === 'function') this.once('finish', callback);
this._ended = true;
this._send();
return this;
}
abort() {
this._aborted = true;
this.emit('abort');
this.destroy();
}
destroy(error) {
this._aborted = true;
if (error) this.emit('error', error);
this.emit('close');
return this;
}
setTimeout(ms, callback) {
if (typeof callback === 'function') this.once('timeout', callback);
this._timeoutMs = ms;
return this;
}
setNoDelay() { return this; }
setSocketKeepAlive() { return this; }
flushHeaders() {}
getHeader(_name) { return undefined; }
setHeader(_name, _value) { return this; }
removeHeader(_name) {}
_send() {
const opts = this._options;
const protocol = opts.protocol || 'http:';
const hostname = opts.hostname || opts.host || 'localhost';
const port = opts.port ? `:${opts.port}` : '';
const path = opts.path || '/';
const url = `${protocol}//${hostname}${port}${path}`;
const headers = {};
if (opts.headers) {
for (const [k, v] of Object.entries(opts.headers)) {
headers[k.toLowerCase()] = String(v);
}
}
const body = this._body.length > 0 ? this._body.join('') : undefined;
const method = (opts.method || 'GET').toUpperCase();
const request = { url, method, headers };
if (body) request.body = body;
if (this._timeoutMs) request.timeout = this._timeoutMs;
// Use pi.http() hostcall if available
if (typeof globalThis.pi === 'object' && typeof globalThis.pi.http === 'function') {
try {
const promise = globalThis.pi.http(request);
if (promise && typeof promise.then === 'function') {
promise.then(
(result) => this._handleResponse(result),
(err) => this.emit('error', typeof err === 'string' ? new Error(err) : err)
);
} else {
this._handleResponse(promise);
}
} catch (err) {
this.emit('error', err);
}
} else {
// No pi.http available — emit error
this.emit('error', new Error('HTTP requests require pi.http() hostcall'));
}
this.emit('finish');
}
_handleResponse(result) {
if (!result || typeof result !== 'object') {
this.emit('error', new Error('Invalid HTTP response from hostcall'));
return;
}
const statusCode = result.status || result.statusCode || 200;
const headers = result.headers || {};
const body = result.body || result.data || '';
const res = new IncomingMessage(statusCode, headers, body);
this.emit('response', res);
// Deliver body asynchronously (in next microtask)
Promise.resolve().then(() => res._deliver());
}
}
// ─── Module API ──────────────────────────────────────────────────────────────
function _parseOptions(input, options) {
if (typeof input === 'string') {
try {
const url = new URL(input);
return {
protocol: url.protocol,
hostname: url.hostname,
port: url.port || undefined,
path: url.pathname + url.search,
...(options || {}),
};
} catch (_e) {
return { path: input, ...(options || {}) };
}
}
if (input && typeof input === 'object' && !(input instanceof URL)) {
return input;
}
if (input instanceof URL) {
return {
protocol: input.protocol,
hostname: input.hostname,
port: input.port || undefined,
path: input.pathname + input.search,
...(options || {}),
};
}
return options || {};
}
export function request(input, optionsOrCallback, callback) {
let options;
if (typeof optionsOrCallback === 'function') {
callback = optionsOrCallback;
options = _parseOptions(input);
} else {
options = _parseOptions(input, optionsOrCallback);
}
if (!options.protocol) options.protocol = 'http:';
return new ClientRequest(options, callback);
}
export function get(input, optionsOrCallback, callback) {
const req = request(input, optionsOrCallback, callback);
req.end();
return req;
}
export function createServer() {
throw new Error('node:http.createServer is not available in PiJS');
}
export { STATUS_CODES, METHODS, IncomingMessage, ClientRequest };
export default { request, get, createServer, STATUS_CODES, METHODS, IncomingMessage, ClientRequest };
"#;Expand description
The JS source for the node:http virtual module.