1pub const NODE_HTTP_JS: &str = r#"
10import EventEmitter from "node:events";
11
12// ─── STATUS_CODES ────────────────────────────────────────────────────────────
13
14const STATUS_CODES = {
15 200: 'OK', 201: 'Created', 204: 'No Content',
16 301: 'Moved Permanently', 302: 'Found', 304: 'Not Modified',
17 400: 'Bad Request', 401: 'Unauthorized', 403: 'Forbidden',
18 404: 'Not Found', 405: 'Method Not Allowed', 408: 'Request Timeout',
19 500: 'Internal Server Error', 502: 'Bad Gateway', 503: 'Service Unavailable',
20};
21
22const METHODS = [
23 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT',
24 'OPTIONS', 'TRACE', 'PATCH',
25];
26
27function __pi_http_is_binary_chunk(chunk) {
28 if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(chunk)) {
29 return true;
30 }
31 if (chunk instanceof Uint8Array || chunk instanceof ArrayBuffer) {
32 return true;
33 }
34 return !!(ArrayBuffer.isView && ArrayBuffer.isView(chunk));
35}
36
37function __pi_http_to_uint8(chunk) {
38 if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(chunk)) {
39 return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
40 }
41 if (chunk instanceof Uint8Array) {
42 return chunk;
43 }
44 if (chunk instanceof ArrayBuffer) {
45 return new Uint8Array(chunk);
46 }
47 if (ArrayBuffer.isView && ArrayBuffer.isView(chunk)) {
48 return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
49 }
50 return new TextEncoder().encode(String(chunk ?? ''));
51}
52
53function __pi_http_clone_body_chunk(chunk) {
54 const view = __pi_http_to_uint8(chunk);
55 if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
56 return Buffer.from(view);
57 }
58 return new Uint8Array(view);
59}
60
61function __pi_http_chunks_to_base64(chunks) {
62 const parts = chunks.map((chunk) => __pi_http_to_uint8(chunk));
63 const total = parts.reduce((sum, part) => sum + part.byteLength, 0);
64 const merged =
65 typeof Buffer !== 'undefined' && typeof Buffer.alloc === 'function'
66 ? Buffer.alloc(total)
67 : new Uint8Array(total);
68
69 let offset = 0;
70 for (const part of parts) {
71 merged.set(part, offset);
72 offset += part.byteLength;
73 }
74
75 if (typeof globalThis.__pi_base64_encode_bytes_native === 'function') {
76 return __pi_base64_encode_bytes_native(merged);
77 }
78
79 // Fallback for older runtime bounds
80 let binary = '';
81 let chunk = [];
82 for (let i = 0; i < merged.length; i++) {
83 chunk.push(merged[i]);
84 if (chunk.length >= 4096) {
85 binary += String.fromCharCode.apply(null, chunk);
86 chunk.length = 0;
87 }
88 }
89 if (chunk.length > 0) {
90 binary += String.fromCharCode.apply(null, chunk);
91 }
92 return __pi_base64_encode_native(binary);
93}
94
95function __pi_http_decode_body_bytes(bodyBytes) {
96 const encoded = String(bodyBytes ?? '');
97 const binary = globalThis.__pi_base64_decode_native(encoded);
98 const out =
99 typeof Buffer !== 'undefined' && typeof Buffer.alloc === 'function'
100 ? Buffer.alloc(binary.length)
101 : new Uint8Array(binary.length);
102
103 for (let i = 0; i < binary.length; i++) {
104 out[i] = binary.charCodeAt(i) & 0xff;
105 }
106 return out;
107}
108
109function __pi_http_decode_chunk(chunk, encoding) {
110 if (!encoding || typeof chunk === 'string') {
111 return chunk;
112 }
113
114 const bytes = __pi_http_to_uint8(chunk);
115 if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
116 return Buffer.from(bytes).toString(encoding);
117 }
118 return new TextDecoder(encoding).decode(bytes);
119}
120
121// ─── IncomingMessage ─────────────────────────────────────────────────────────
122
123class IncomingMessage extends EventEmitter {
124 constructor(statusCode, headers, body) {
125 super();
126 this.statusCode = statusCode;
127 this.statusMessage = STATUS_CODES[statusCode] || 'Unknown';
128 this.headers = headers || {};
129 this._body = body || '';
130 this._destroyed = false;
131 this.complete = false;
132 this.httpVersion = '1.1';
133 this.method = null;
134 this.url = '';
135 }
136
137 _deliver() {
138 if (this._destroyed) {
139 return;
140 }
141
142 const chunk = __pi_http_decode_chunk(this._body, this._encoding);
143 if (chunk && chunk.length > 0) {
144 this.emit('data', chunk);
145 }
146
147 if (this._destroyed) {
148 return;
149 }
150
151 this.complete = true;
152 this.emit('end');
153 }
154
155 setEncoding(encoding) {
156 this._encoding = encoding ? String(encoding) : 'utf8';
157 return this;
158 }
159 resume() { return this; }
160 pause() { return this; }
161 destroy() {
162 if (this._destroyed) {
163 return this;
164 }
165 this._destroyed = true;
166 this.emit('close');
167 return this;
168 }
169}
170
171// ─── ClientRequest ───────────────────────────────────────────────────────────
172
173class ClientRequest extends EventEmitter {
174 constructor(options, callback) {
175 super();
176 this._options = options;
177 this._body = [];
178 this._ended = false;
179 this._aborted = false;
180 this._headers = {};
181 this.socket = { remoteAddress: '127.0.0.1', remotePort: 0 };
182 this.method = options.method || 'GET';
183 this.path = options.path || '/';
184
185 if (options.headers) {
186 for (const [k, v] of Object.entries(options.headers)) {
187 this._headers[String(k).toLowerCase()] = String(v);
188 }
189 }
190
191 if (typeof callback === 'function') {
192 this.once('response', callback);
193 }
194 }
195
196 write(chunk) {
197 if (!this._ended && !this._aborted) {
198 this._body.push(
199 __pi_http_is_binary_chunk(chunk)
200 ? __pi_http_clone_body_chunk(chunk)
201 : String(chunk)
202 );
203 }
204 return true;
205 }
206
207 end(chunk, _encoding, callback) {
208 if (typeof chunk === 'function') { callback = chunk; chunk = undefined; }
209 if (typeof _encoding === 'function') { callback = _encoding; }
210 if (chunk) this.write(chunk);
211 if (typeof callback === 'function') this.once('finish', callback);
212
213 this._ended = true;
214 this._send();
215 return this;
216 }
217
218 abort() {
219 this._aborted = true;
220 this.emit('abort');
221 this.destroy();
222 }
223
224 destroy(error) {
225 this._aborted = true;
226 if (error) this.emit('error', error);
227 this.emit('close');
228 return this;
229 }
230
231 setTimeout(ms, callback) {
232 if (typeof callback === 'function') this.once('timeout', callback);
233 this._timeoutMs = ms;
234 return this;
235 }
236
237 setNoDelay() { return this; }
238 setSocketKeepAlive() { return this; }
239 flushHeaders() {}
240 getHeader(name) { return this._headers[String(name).toLowerCase()]; }
241 setHeader(name, value) {
242 if (!this._ended && !this._aborted) {
243 this._headers[String(name).toLowerCase()] = String(value);
244 }
245 return this;
246 }
247 removeHeader(name) {
248 if (!this._ended && !this._aborted) {
249 delete this._headers[String(name).toLowerCase()];
250 }
251 return this;
252 }
253
254 _send() {
255 if (this._aborted) {
256 return;
257 }
258
259 const opts = this._options;
260 const protocol = opts.protocol || 'http:';
261 const hostname = opts.hostname || opts.host || 'localhost';
262 const port = opts.port ? `:${opts.port}` : '';
263 const path = opts.path || '/';
264 const url = `${protocol}//${hostname}${port}${path}`;
265
266 const headers = { ...this._headers };
267
268 const method = (opts.method || 'GET').toUpperCase();
269 const request = { url, method, headers };
270 if (this._body.length > 0) {
271 const hasBinaryChunk = this._body.some((chunk) => __pi_http_is_binary_chunk(chunk));
272 if (hasBinaryChunk) {
273 request.body_bytes = __pi_http_chunks_to_base64(this._body);
274 } else {
275 request.body = this._body.join('');
276 }
277 }
278 if (this._timeoutMs) request.timeout = this._timeoutMs;
279
280 // Use pi.http() hostcall if available
281 if (typeof globalThis.pi === 'object' && typeof globalThis.pi.http === 'function') {
282 try {
283 const promise = globalThis.pi.http(request);
284 if (promise && typeof promise.then === 'function') {
285 promise.then(
286 (result) => {
287 if (!this._aborted) {
288 this._handleResponse(result);
289 }
290 },
291 (err) => {
292 if (!this._aborted) {
293 this.emit('error', typeof err === 'string' ? new Error(err) : err);
294 }
295 }
296 );
297 } else {
298 this._handleResponse(promise);
299 }
300 } catch (err) {
301 this.emit('error', err);
302 }
303 } else {
304 // No pi.http available — emit error
305 this.emit('error', new Error('HTTP requests require pi.http() hostcall'));
306 }
307
308 this.emit('finish');
309 }
310
311 _handleResponse(result) {
312 if (this._aborted) {
313 return;
314 }
315
316 if (!result || typeof result !== 'object') {
317 this.emit('error', new Error('Invalid HTTP response from hostcall'));
318 return;
319 }
320
321 const statusCode = result.status || result.statusCode || 200;
322 const headers = result.headers || {};
323 const body =
324 result.body_bytes !== undefined && result.body_bytes !== null
325 ? __pi_http_decode_body_bytes(result.body_bytes)
326 : (result.body || result.data || '');
327
328 const res = new IncomingMessage(statusCode, headers, body);
329 this.emit('response', res);
330 // Deliver body asynchronously (in next microtask)
331 Promise.resolve().then(() => {
332 if (!this._aborted) {
333 res._deliver();
334 }
335 });
336 }
337}
338
339// ─── Module API ──────────────────────────────────────────────────────────────
340
341function _parseOptions(input, options) {
342 if (typeof input === 'string') {
343 try {
344 const url = new URL(input);
345 return {
346 protocol: url.protocol,
347 hostname: url.hostname,
348 port: url.port || undefined,
349 path: url.pathname + url.search,
350 ...(options || {}),
351 };
352 } catch (_e) {
353 return { path: input, ...(options || {}) };
354 }
355 }
356 if (input && typeof input === 'object' && !(input instanceof URL)) {
357 return { ...input };
358 }
359 if (input instanceof URL) {
360 return {
361 protocol: input.protocol,
362 hostname: input.hostname,
363 port: input.port || undefined,
364 path: input.pathname + input.search,
365 ...(options || {}),
366 };
367 }
368 return options || {};
369}
370
371export function request(input, optionsOrCallback, callback) {
372 let options;
373 if (typeof optionsOrCallback === 'function') {
374 callback = optionsOrCallback;
375 options = _parseOptions(input);
376 } else {
377 options = _parseOptions(input, optionsOrCallback);
378 }
379 if (!options.protocol) options.protocol = 'http:';
380 return new ClientRequest(options, callback);
381}
382
383export function get(input, optionsOrCallback, callback) {
384 const req = request(input, optionsOrCallback, callback);
385 req.end();
386 return req;
387}
388
389export function createServer() {
390 throw new Error('node:http.createServer is not available in PiJS');
391}
392
393export { STATUS_CODES, METHODS, IncomingMessage, ClientRequest };
394export default { request, get, createServer, STATUS_CODES, METHODS, IncomingMessage, ClientRequest };
395"#;
396
397pub const NODE_HTTPS_JS: &str = r#"
399import EventEmitter from "node:events";
400import * as http from "node:http";
401
402export function request(input, optionsOrCallback, callback) {
403 let options;
404 if (typeof optionsOrCallback === 'function') {
405 callback = optionsOrCallback;
406 options = typeof input === 'string' || input instanceof URL
407 ? { ...(typeof input === 'string' ? (() => { try { const u = new URL(input); return { protocol: u.protocol, hostname: u.hostname, port: u.port, path: u.pathname + u.search }; } catch(_) { return { path: input }; } })() : { protocol: input.protocol, hostname: input.hostname, port: input.port, path: input.pathname + input.search }) }
408 : { ...(input || {}) };
409 } else {
410 options = typeof input === 'string' || input instanceof URL
411 ? { ...(typeof input === 'string' ? (() => { try { const u = new URL(input); return { protocol: u.protocol, hostname: u.hostname, port: u.port, path: u.pathname + u.search }; } catch(_) { return { path: input }; } })() : { protocol: input.protocol, hostname: input.hostname, port: input.port, path: input.pathname + input.search }), ...(optionsOrCallback || {}) }
412 : { ...(input || {}), ...(optionsOrCallback || {}) };
413 }
414 if (!options.protocol) options.protocol = 'https:';
415 return http.request(options, callback);
416}
417
418export function get(input, optionsOrCallback, callback) {
419 const req = request(input, optionsOrCallback, callback);
420 req.end();
421 return req;
422}
423
424export function createServer() {
425 throw new Error('node:https.createServer is not available in PiJS');
426}
427
428export const globalAgent = {};
429
430export default { request, get, createServer, globalAgent };
431"#;