serpapi
Version:
Scrape and parse search engine results using SerpApi.
98 lines (97 loc) • 3.34 kB
JavaScript
import { version } from "../version.js";
import https from "https";
import qs from "querystring";
import process from "process";
import { RequestTimeoutError } from "./errors.js";
import { config } from "./config.js";
/**
* This `_internals` object is needed to support stubbing/spying of
* certain functions in this file.
* https://deno.land/manual@v1.28.3/basics/testing/mocking
*
* It's also useful to encapsulate functions that are polyfilled.
*/
export const _internals = {
execute: execute,
getHostnameAndPort: getHostnameAndPort,
};
/** Facilitates stubbing in tests */
function getHostnameAndPort() {
return {
hostname: "serpapi.com",
port: 443,
};
}
export function getSource() {
var _a, _b;
const moduleSource = `serpapi@${version}`;
if (typeof Deno == "object") {
const denoVersion = (_a = Deno.version) === null || _a === void 0 ? void 0 : _a.deno;
if (denoVersion) {
return `deno@${denoVersion},${moduleSource}`;
}
}
else if (typeof process == "object") {
const nodeVersion = (_b = process.versions) === null || _b === void 0 ? void 0 : _b.node;
if (nodeVersion) {
return `nodejs@${nodeVersion},${moduleSource}`;
}
}
return `nodejs,${moduleSource}`;
}
export function buildRequestOptions(path, parameters) {
const clonedParams = Object.assign({}, parameters);
for (const k in clonedParams) {
if (k === "requestOptions" ||
k === "timeout" ||
clonedParams[k] === undefined) {
delete clonedParams[k];
}
}
const basicOptions = Object.assign(Object.assign({}, _internals.getHostnameAndPort()), { path: `${path}?${qs.stringify(clonedParams)}`, method: "GET" });
return Object.assign(Object.assign(Object.assign({}, config.requestOptions), parameters.requestOptions), basicOptions);
}
export function execute(path, parameters, timeout) {
const options = buildRequestOptions(path, Object.assign(Object.assign({}, parameters), { source: getSource() }));
return new Promise((resolve, reject) => {
let timer;
const handleResponse = (resp) => {
resp.setEncoding("utf8");
let data = "";
// A chunk of data has been received
resp.on("data", (chunk) => {
data += chunk;
});
// The whole response has been received
resp.on("end", () => {
try {
if (resp.statusCode == 200) {
resolve(data);
}
else {
reject(data);
}
}
catch (e) {
reject(e);
}
finally {
if (timer)
clearTimeout(timer);
}
});
};
const handleError = (err) => {
reject(err);
if (timer)
clearTimeout(timer);
};
const req = https.get(options, handleResponse).on("error", handleError);
if (timeout > 0) {
timer = setTimeout(() => {
reject(new RequestTimeoutError());
req.destroy();
}, timeout);
}
});
}