@translated/lara
Version:
Official Lara SDK for JavaScript and Node.js
377 lines (376 loc) • 14.6 kB
JavaScript
;
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;