UNPKG

@translated/lara

Version:

Official Lara SDK for JavaScript and Node.js

377 lines (376 loc) 14.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NodeClient = exports.NodeLaraClient = void 0; const node_fs_1 = __importDefault(require("node:fs")); const node_http_1 = __importDefault(require("node:http")); const node_https_1 = __importDefault(require("node:https")); const node_stream_1 = require("node:stream"); const node_timers_1 = require("node:timers"); const form_data_1 = __importDefault(require("form-data")); const errors_1 = require("../../errors"); const client_1 = require("./client"); /** @internal */ class NodeLaraClient extends client_1.LaraClient { constructor(baseUrl, auth, keepAlive, timeout) { super(auth); this.baseUrl = baseUrl; this.agent = baseUrl.secure ? new node_https_1.default.Agent({ keepAlive }) : new node_http_1.default.Agent({ keepAlive }); this.timeout = timeout; } async send(method, path, headers, body, streamResponse) { let requestBody; if (body) { if (headers["Content-Type"] === "multipart/form-data") { const formBody = new form_data_1.default(); for (const [key, value] of Object.entries(body)) { if (!value) continue; if (Array.isArray(value)) { for (const v of value) formBody.append(key, v); } else { formBody.append(key, value); } } headers = { ...headers, ...formBody.getHeaders() }; requestBody = formBody; } else { requestBody = JSON.stringify(body, undefined, 0); } } return new Promise((resolve, reject) => { const options = { host: this.baseUrl.hostname, port: this.baseUrl.port, path: path, method, headers: headers, agent: this.agent }; let hardTimeout = null; const req = (this.baseUrl.secure ? node_https_1.default : node_http_1.default).request(options, (res) => { let data = ""; if (streamResponse && res.statusCode >= 200 && res.statusCode < 300) { hardTimeout && (0, node_timers_1.clearTimeout)(hardTimeout); return resolve({ statusCode: res.statusCode, body: res, headers: res.headers }); } // biome-ignore lint/suspicious/noAssignInExpressions: store response data res.on("data", (chunk) => (data += chunk)); res.on("end", () => { var _a; hardTimeout && (0, node_timers_1.clearTimeout)(hardTimeout); let json; if ((_a = res.headers["content-type"]) === null || _a === void 0 ? void 0 : _a.includes("application/json")) { try { json = JSON.parse(data); } catch (_e) { return reject(new SyntaxError("Invalid JSON response")); } return resolve({ statusCode: res.statusCode, body: json, headers: res.headers }); } else { return resolve({ statusCode: res.statusCode, body: data, headers: res.headers }); } }); res.on("error", (err) => { hardTimeout && (0, node_timers_1.clearTimeout)(hardTimeout); if (err instanceof errors_1.TimeoutError) return reject(err); req.destroy(); return reject(err); }); }); req.on("error", (err) => { hardTimeout && (0, node_timers_1.clearTimeout)(hardTimeout); if (err instanceof errors_1.TimeoutError) return reject(err); req.destroy(); return reject(err); }); // Set timeout if provided and positive if (this.timeout && this.timeout > 0) { hardTimeout = setTimeout(() => { req.destroy(new errors_1.TimeoutError(`Request timed out after ${this.timeout}ms`)); }, this.timeout); } if (requestBody instanceof form_data_1.default) { requestBody.pipe(req); } else if (requestBody) { req.write(requestBody); req.end(); } else { req.end(); } }); } async *sendAndGetStream(method, path, headers, body) { let requestBody; if (body) { if (headers["Content-Type"] === "multipart/form-data") { const formBody = new form_data_1.default(); for (const [key, value] of Object.entries(body)) { if (!value) continue; if (Array.isArray(value)) { for (const v of value) formBody.append(key, v); } else { formBody.append(key, value); } } headers = { ...headers, ...formBody.getHeaders() }; requestBody = formBody; } else { requestBody = JSON.stringify(body, undefined, 0); } } const options = { host: this.baseUrl.hostname, port: this.baseUrl.port, path: path, method, headers: headers, agent: this.agent }; let hardTimeout = null; // Create async iterator from the stream const chunks = []; let resolveChunk = null; let streamEnded = false; let streamError = null; const req = (this.baseUrl.secure ? node_https_1.default : node_http_1.default).request(options, (res) => { let buffer = ""; res.on("data", (chunk) => { buffer += chunk.toString(); const lines = buffer.split("\n"); buffer = lines.pop() || ""; // Keep incomplete line in buffer for (const line of lines) { if (line.trim()) { try { const json = JSON.parse(line); chunks.push({ statusCode: json.status || res.statusCode, body: json.data || json, headers: res.headers }); if (resolveChunk) { resolveChunk(); resolveChunk = null; } } catch (_e) { // Skip invalid JSON lines } } } }); res.on("end", () => { hardTimeout && (0, node_timers_1.clearTimeout)(hardTimeout); // Process any remaining data in buffer if (buffer.trim()) { try { const json = JSON.parse(buffer); chunks.push({ statusCode: json.status || res.statusCode, body: json.data || json, headers: res.headers }); } catch (_e) { // Skip invalid JSON } } streamEnded = true; if (resolveChunk) { resolveChunk(); resolveChunk = null; } }); res.on("error", (err) => { hardTimeout && (0, node_timers_1.clearTimeout)(hardTimeout); if (err instanceof errors_1.TimeoutError) throw err; req.destroy(); streamError = err; if (resolveChunk) { resolveChunk(); resolveChunk = null; } }); }); req.on("error", (err) => { hardTimeout && (0, node_timers_1.clearTimeout)(hardTimeout); if (err instanceof errors_1.TimeoutError) throw err; streamError = err; if (resolveChunk) { resolveChunk(); resolveChunk = null; } }); // Set timeout if provided and positive if (this.timeout && this.timeout > 0) { hardTimeout = setTimeout(() => { req.destroy(new errors_1.TimeoutError(`Request timed out after ${this.timeout}ms`)); }, this.timeout); } if (requestBody instanceof form_data_1.default) { requestBody.pipe(req); } else if (requestBody) { req.write(requestBody); req.end(); } else { req.end(); } // Yield chunks as they arrive while (true) { if (streamError) { throw streamError; } if (chunks.length > 0) { yield chunks.shift(); } else if (streamEnded) { break; } else { // Wait for next chunk await new Promise((resolve) => { resolveChunk = resolve; }); } } } wrapMultiPartFile(file) { if (typeof file === "string") file = node_fs_1.default.createReadStream(file); if (file instanceof node_stream_1.Readable) return file; throw new TypeError(`Invalid file input in Node.js. Expected a Readable stream or a valid file path, but received ${typeof file}.`); } } exports.NodeLaraClient = NodeLaraClient; // biome-ignore lint/complexity/noStaticOnlyClass: used as a namespace for HTTP client methods class NodeClient { static get(url, headers) { return NodeClient.send("GET", url, headers); } static async send(method, url, headers, body) { const _url = new URL(url); if (_url.protocol !== "https:" && _url.protocol !== "http:") throw new TypeError(`Invalid URL (protocol): ${_url.protocol}`); const parsedURL = { secure: _url.protocol === "https:", hostname: _url.hostname, port: _url.port ? parseInt(_url.port, 10) : _url.protocol === "https:" ? 443 : 80 }; const path = _url.pathname + _url.search + _url.hash; const agent = parsedURL.secure ? new node_https_1.default.Agent({ keepAlive: true }) : new node_http_1.default.Agent({ keepAlive: true }); let requestBody; if (body) { if (headers && headers["Content-Type"] === "multipart/form-data") { const formBody = new form_data_1.default(); for (const [key, value] of Object.entries(body)) { if (!value) continue; if (Array.isArray(value)) { for (const v of value) formBody.append(key, v); } else { formBody.append(key, value); } } headers = { ...headers, ...formBody.getHeaders() }; requestBody = formBody; } else { requestBody = JSON.stringify(body, undefined, 0); } } return new Promise((resolve, reject) => { const options = { host: parsedURL.hostname, port: parsedURL.port, path, method, headers, agent }; const req = (parsedURL.secure ? node_https_1.default : node_http_1.default).request(options, (res) => { let data = ""; // biome-ignore lint/suspicious/noAssignInExpressions: store response data res.on("data", (chunk) => (data += chunk)); res.on("end", () => { var _a; let json; if ((_a = res.headers["content-type"]) === null || _a === void 0 ? void 0 : _a.includes("application/json")) { try { json = JSON.parse(data); } catch (_e) { return reject(new SyntaxError("Invalid JSON response")); } return resolve({ statusCode: res.statusCode, body: json, headers: res.headers }); } else { return resolve({ statusCode: res.statusCode, body: data, headers: res.headers }); } }); }); req.on("error", reject); if (requestBody instanceof form_data_1.default) { requestBody.pipe(req); } else if (requestBody) { req.write(requestBody); req.end(); } else { req.end(); } }); } } exports.NodeClient = NodeClient;