UNPKG

link-preview-js

Version:

Javascript module to extract and fetch HTTP link information from blocks of text.

94 lines (93 loc) 5.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getPreviewFromContent = void 0; exports.getLinkPreview = getLinkPreview; const constants_1 = require("./constants"); const shared_1 = require("./shared"); var shared_2 = require("./shared"); Object.defineProperty(exports, "getPreviewFromContent", { enumerable: true, get: function () { return shared_2.getPreviewFromContent; } }); /** * Parses the text, extracts the first link it finds and does a HTTP request * to fetch the website content, afterwards it tries to parse the internal HTML * and extract the information via meta tags. * * This is the mobile/browser-safe entrypoint: no Node-only dependencies, and no * `resolveDNSHost` SSRF protection (see ILinkPreviewOptions above for why). * @param text string, text to be parsed * @param options ILinkPreviewOptions */ function getLinkPreview(text, options) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; if (!text || typeof text !== `string`) { throw new Error(`link-preview-js did not receive a valid url or text`); } const detectedUrl = text .replace(/\n/g, ` `) .split(` `) .find((token) => constants_1.CONSTANTS.REGEX_VALID_URL.test(token)); if (!detectedUrl) { throw new Error(`link-preview-js did not receive a valid a url or text`); } if ((options === null || options === void 0 ? void 0 : options.followRedirects) === `manual` && !(options === null || options === void 0 ? void 0 : options.handleRedirects)) { throw new Error(`link-preview-js followRedirects is set to manual, but no handleRedirects function was provided`); } const timeout = (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : 3000; // 3 second timeout default const controller = new AbortController(); const timeoutCounter = setTimeout(() => controller.abort(), timeout); const fetchOptions = { headers: (_b = options === null || options === void 0 ? void 0 : options.headers) !== null && _b !== void 0 ? _b : {}, redirect: (_c = options === null || options === void 0 ? void 0 : options.followRedirects) !== null && _c !== void 0 ? _c : `error`, signal: controller.signal, }; const fetchUrl = (options === null || options === void 0 ? void 0 : options.proxyUrl) ? options.proxyUrl.concat(detectedUrl) : detectedUrl; try { const fetchWithTimeout = (url) => __awaiter(this, void 0, void 0, function* () { return fetch(url, fetchOptions).catch((e) => { if (e.name === `AbortError`) { throw new Error(`Request timeout`); } clearTimeout(timeoutCounter); throw e; }); }); let response = yield fetchWithTimeout(fetchUrl); if (response.status > 300 && response.status < 309 && fetchOptions.redirect === `manual` && (options === null || options === void 0 ? void 0 : options.handleRedirects)) { const locationHeader = response.headers.get(`location`) || ``; const isAbsoluteURI = locationHeader.startsWith("http://") || locationHeader.startsWith("https://"); // Resolve the URL, handling both absolute and relative URLs const forwardedUrl = isAbsoluteURI ? locationHeader : new URL(locationHeader, fetchUrl).href; if (!options.handleRedirects(fetchUrl, forwardedUrl)) { throw new Error(`link-preview-js could not handle redirect`); } response = yield fetchWithTimeout(forwardedUrl); } clearTimeout(timeoutCounter); const headers = {}; response.headers.forEach((header, key) => { headers[key] = header; }); const normalizedResponse = { url: (options === null || options === void 0 ? void 0 : options.proxyUrl) ? response.url.replace(options.proxyUrl, ``) : response.url, headers, data: yield response.text(), }; return (0, shared_1.parseResponse)(normalizedResponse, options); } finally { clearTimeout(timeoutCounter); } }); }