UNPKG

petcarescript

Version:

PetCareScript - A modern, expressive programming language designed for humans

274 lines (229 loc) 9.16 kB
/** * PetCareScript HTTP Library * HTTP client and server functionality */ const http = require('http'); const https = require('https'); const url = require('url'); const querystring = require('querystring'); class HTTPClient { constructor() { this.defaultHeaders = { 'User-Agent': 'PetCareScript-HTTP/1.2.3' }; this.timeout = 30000; // 30 seconds default timeout } async request(method, urlString, options = {}) { return new Promise((resolve, reject) => { const urlObj = url.parse(urlString); const isHttps = urlObj.protocol === 'https:'; const client = isHttps ? https : http; const requestOptions = { hostname: urlObj.hostname, port: urlObj.port || (isHttps ? 443 : 80), path: urlObj.path, method: method.toUpperCase(), headers: { ...this.defaultHeaders, ...options.headers }, timeout: options.timeout || this.timeout }; if (options.body && typeof options.body === 'object') { const jsonData = JSON.stringify(options.body); requestOptions.headers['Content-Type'] = 'application/json'; requestOptions.headers['Content-Length'] = Buffer.byteLength(jsonData); } const req = client.request(requestOptions, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { let parsedData = data; try { if (res.headers['content-type'] && res.headers['content-type'].includes('application/json')) { parsedData = JSON.parse(data); } } catch (e) { // Keep as string if JSON parsing fails } resolve({ status: res.statusCode, statusText: res.statusMessage, headers: res.headers, data: parsedData, size: Buffer.byteLength(data), url: urlString }); }); }); req.on('error', (error) => { reject(new Error(`HTTP Request failed: ${error.message}`)); }); req.on('timeout', () => { req.abort(); reject(new Error(`HTTP Request timeout after ${requestOptions.timeout}ms`)); }); if (options.body) { if (typeof options.body === 'object') { req.write(JSON.stringify(options.body)); } else { req.write(options.body); } } req.end(); }); } async get(url, options = {}) { return this.request('GET', url, options); } async post(url, data, options = {}) { return this.request('POST', url, { ...options, body: data }); } async put(url, data, options = {}) { return this.request('PUT', url, { ...options, body: data }); } async patch(url, data, options = {}) { return this.request('PATCH', url, { ...options, body: data }); } async delete(url, options = {}) { return this.request('DELETE', url, options); } // Método para fazer múltiplas requisições em paralelo async parallel(requests) { return Promise.all(requests.map(req => { const { method, url, data, options } = req; switch (method.toUpperCase()) { case 'GET': return this.get(url, options); case 'POST': return this.post(url, data, options); case 'PUT': return this.put(url, data, options); case 'PATCH': return this.patch(url, data, options); case 'DELETE': return this.delete(url, options); default: throw new Error(`Unsupported method: ${method}`); } })); } } // Middleware melhorados const Middleware = { cors: (options = {}) => { const { origin = '*', methods = 'GET,HEAD,PUT,PATCH,POST,DELETE', allowedHeaders = 'Content-Type,Authorization', credentials = false, maxAge = 86400 // 24 hours } = options; return { name: 'CORS', type: 'middleware', arity: () => 3, call: (interpreter, args) => { const [req, res, next] = args; res.headers = res.headers || {}; res.headers['Access-Control-Allow-Origin'] = origin; res.headers['Access-Control-Allow-Methods'] = methods; res.headers['Access-Control-Allow-Headers'] = allowedHeaders; res.headers['Access-Control-Max-Age'] = maxAge; if (credentials) { res.headers['Access-Control-Allow-Credentials'] = 'true'; } if (req.method === 'OPTIONS') { res.statusCode = 204; return res.send(''); } if (next) next(); } }; }, json: (options = {}) => { const { limit = '100kb', strict = true } = options; return { name: 'JSON', type: 'middleware', arity: () => 3, call: (interpreter, args) => { const [req, res, next] = args; if (req.headers['content-type'] && req.headers['content-type'].includes('application/json')) { // JSON parsing would be handled here req.body = req.body || {}; } if (next) next(); } }; }, logger: (format = 'combined') => { return { name: 'Logger', type: 'middleware', arity: () => 3, call: (interpreter, args) => { const [req, res, next] = args; const timestamp = new Date().toISOString(); switch (format) { case 'tiny': console.log(`${req.method} ${req.path} ${res.statusCode || 200}`); break; case 'combined': default: console.log(`[${timestamp}] ${req.method} ${req.path} - ${req.ip || '127.0.0.1'}`); break; } if (next) next(); } }; }, rateLimit: (options = {}) => { const { windowMs = 15 * 60 * 1000, max = 100 } = options; const requests = new Map(); return { name: 'RateLimit', type: 'middleware', arity: () => 3, call: (interpreter, args) => { const [req, res, next] = args; const ip = req.ip || '127.0.0.1'; const now = Date.now(); if (!requests.has(ip)) { requests.set(ip, []); } const userRequests = requests.get(ip); const recentRequests = userRequests.filter(time => now - time < windowMs); if (recentRequests.length >= max) { res.statusCode = 429; return res.json({ error: 'Too many requests' }); } recentRequests.push(now); requests.set(ip, recentRequests); if (next) next(); } }; } }; const HTTPLib = { client: new HTTPClient(), middleware: Middleware, // Factory functions createClient: (options = {}) => { const client = new HTTPClient(); if (options.timeout) client.timeout = options.timeout; if (options.headers) Object.assign(client.defaultHeaders, options.headers); return client; }, // Direct HTTP methods get: (url, options) => new HTTPClient().get(url, options), post: (url, data, options) => new HTTPClient().post(url, data, options), put: (url, data, options) => new HTTPClient().put(url, data, options), patch: (url, data, options) => new HTTPClient().patch(url, data, options), delete: (url, options) => new HTTPClient().delete(url, options), // Utility functions parseURL: (urlString) => url.parse(urlString), buildURL: (baseURL, params) => { const urlObj = new URL(baseURL); Object.entries(params).forEach(([key, value]) => { urlObj.searchParams.set(key, value); }); return urlObj.toString(); } }; module.exports = HTTPLib;