UNPKG

mhtml-to-html

Version:

JavaScript library for parsing MHTML files and converting them into single HTML files

652 lines (640 loc) 32.5 kB
/* global URL */ import { decodeMimeHeader, parseDOM, decodeBase64, decodeBinary, getCharset, getResourceURI, resolvePath, isStylesheet, EVENT_HANDLER_ATTRIBUTES } from "./util.js"; import * as cssTree from "./vendor/csstree.esm.js"; import * as srcsetParser from "./srcset-parser.js"; const BASE64_ENCODING = "base64"; const HREF_ATTRIBUTE = "href"; const SRC_ATTRIBUTE = "src"; const TITLE_ATTRIBUTE = "title"; const SRCSET_ATTRIBUTE = "srcset"; const SRCDOC_ATTRIBUTE = "srcdoc"; const CONTENT_ATTRIBUTE = "content"; const STYLE_ATTRIBUTE = "style"; const MEDIA_ATTRIBUTE = "media"; const BACKGROUND_ATTRIBUTE = "background"; const REL_ATTRIBUTE = "rel"; const DATA_ATTRIBUTE = "data"; const TYPE_ATTRIBUTE = "type"; const PING_ATTRIBUTE = "ping"; const HTTP_EQUIV_ATTRIBUTE = "http-equiv"; const INTEGRITY_ATTRIBUTE = "integrity"; const CHARSET_ATTRIBUTE = "charset"; const SHADOWMODE_ATTRIBUTE = "shadowmode"; const SHADOWROOTMODE_ATTRIBUTE = "shadowrootmode"; const SIZES_ATTRIBUTE = "sizes"; const STYLESHEET_CONTENT_TYPE = "text/css"; const CID_PROTOCOL = "cid:"; const DATA_PROTOCOL = "data:"; const HTTP_PROTOCOL = "http:"; const HTTPS_PROTOCOL = "https:"; const URN_PROTOCOL = "urn:"; const AT_RULE = "Atrule"; const IMPORT_RULE = "import"; const URL_FUNCTION = "Url"; const STYLESHEET_CONTEXT = "stylesheet"; const DECLARATION_LIST_CONTEXT = "declarationList"; const BASE_TAG = "BASE"; const LINK_TAG = "LINK"; const STYLE_TAG = "STYLE"; const IMG_TAG = "IMG"; const AUDIO_TAG = "AUDIO"; const VIDEO_TAG = "VIDEO"; const SOURCE_TAG = "SOURCE"; const SCRIPT_TAG = "SCRIPT"; const BODY_TAG = "BODY"; const TABLE_TAG = "TABLE"; const TD_TAG = "TD"; const TH_TAG = "TH"; const INPUT_TAG = "INPUT"; const IFRAME_TAG = "IFRAME"; const FRAME_TAG = "FRAME"; const EMBED_TAG = "EMBED"; const OBJECT_TAG = "OBJECT"; const A_TAG = "A"; const AREA_TAG = "AREA"; const META_TAG = "META"; const TEMPLATE_TAG = "TEMPLATE"; const HEAD_TAG = "HEAD"; const TITLE_TAG = "TITLE"; const ORIGINAL_URL_FUNCTION_NAME = "--mhtml-to-html-url"; const ORIGINAL_URL_ATTRIBUTE_PREFIX = "data-original-"; const CONTENT_TYPE_HEADER = "Content-Type"; const REL_ATTRIBUTE_STYLESHEET = "stylesheet"; const REL_ATTRIBUTE_ICON = "icon"; const REL_ATTRIBUTE_CANONICAL = "canonical"; const REL_ATTRIBUTE_ALTERNATE = "alternate"; const HTTP_EQUIV_ATTRIBUTE_REFRESH = "refresh"; const HTTP_EQUIV_ATTRIBUTE_CSP = "content-security-policy"; const TYPE_ATTRIBUTE_IMAGE = "image"; const REL_REMOVED_VALUES_REGEXP = /(preconnect|prerender|dns-prefetch|preload|prefetch|manifest|modulepreload)/gi; const URN_ERROR_REGEXP = /^urn:[^:]+:(.+)$/; const APPLICATION_OCTET_STREAM_CONTENT_TYPE = "application/octet-stream"; const JSON_LD_CONTENT_TYPE = "application/ld+json"; const TEXT_HTML_CONTENT_TYPE = "text/html"; const UTF8_CHARSET = "utf-8"; const INDEX_PAGE_NOT_FOUND_ERROR = "Index page not found"; const CID_REGEXP = /^<.+>$/; const CONTENT_TYPE_SEPARATOR = ";"; const DATA_URI_PAYLOAD_SEPARATOR = ","; const EMPTY_STRING = ""; const ORIGINAL_URL_FUNCTION_REGEXP = /url\(--mhtml-to-html-url\\\(\\"(.*?)\\"\\\)\\ /g; const ORIGINAL_URL_COMMENT = "/* original URL: $1 */ url("; const DEFAULT_CSP = "default-src 'none'; connect-src 'self' data:; font-src 'self' data:; img-src 'self' data:; style-src 'self' 'unsafe-inline' data:; frame-src 'self' data:; media-src 'self' data:; object-src 'self' data:; "; const JS_ENABLED_CSP = "script-src 'self' 'unsafe-inline' data:;"; const JS_DISABLED_CSP = "script-src 'none';"; const CSS_FUNCTION_PARENTHESIS_START = "("; const CSS_FUNCTION_PARENTHESIS_END = ") "; const SUBJECT_HEADER = "Subject"; const DATE_HEADER = "Date"; const FROM_HEADER = "From"; const JSON_LD_PAGE_INFO = { "@context": "https://schema.org", "@type": "WebPage", "additionalProperty": { "@type": "PropertyValue", "name": "savedBy" } }; export default fetchAndConvert; async function fetchAndConvert(mhtml, config, failedResources = []) { if (config.fetchMissingResources) { let { fetch } = config; let missingResources = []; if (!fetch) { fetch = globalThis.fetch; } missingResources = convert(mhtml, config); missingResources = missingResources.filter(resource => !failedResources.includes(resource.id)); if (missingResources.length) { await Promise.all(missingResources.map(async resource => { const { id, transferEncoding } = resource; let url = id; const urnErrorMatch = url.match(URN_ERROR_REGEXP); if (urnErrorMatch) { url = urnErrorMatch[1]; } try { const response = await fetch(url); if (response.ok) { resource.contentType = response.headers.get(CONTENT_TYPE_HEADER) || APPLICATION_OCTET_STREAM_CONTENT_TYPE; if (transferEncoding === BASE64_ENCODING) { const bytes = await response.bytes(); resource.data = decodeBinary(bytes); } else { resource.data = await response.text(); } mhtml.resources[id] = resource; } else if (!failedResources.includes(id)) { failedResources.push(id); } // eslint-disable-next-line no-unused-vars } catch (_) { if (!failedResources.includes(id)) { failedResources.push(id); } } })); return fetchAndConvert(mhtml, config, failedResources); } else { return convert(mhtml, { ...config, fetchMissingResources: false }); } } else { return convert(mhtml, config); } } function convert({ headers, frames, resources, unfoundResources = new Set(), index, id }, { DOMParser, enableScripts, fetchMissingResources } = { DOMParser: globalThis.DOMParser }) { let resource = resources[index]; if (!resource) { throw new Error(INDEX_PAGE_NOT_FOUND_ERROR); } let base = resource.id; if (resource.transferEncoding === BASE64_ENCODING) { resource.transferEncoding = undefined; resource.data = decodeBase64(resource.data, getCharset(resource.contentType)); } const contentType = resource.contentType.split(CONTENT_TYPE_SEPARATOR)[0]; const dom = parseDOM(resource.data, contentType, DOMParser); const document = dom.document; let nodes = [document]; const baseElement = document.getElementsByTagName(BASE_TAG)[0]; if (baseElement) { const href = baseElement.getAttribute(HREF_ATTRIBUTE); if (href) { base = resolvePath(href, base); } baseElement.remove(); } if (!fetchMissingResources) { resource.used = true; } nodes = [document]; let canonicalLinkElement; const stylesheets = {}; const missingResources = []; const removedNodes = []; const favicons = []; let title; while (nodes.length) { const childNode = nodes.shift(); if (childNode.childNodes) { for (const child of childNode.childNodes) { let href, src; if (child.getAttribute) { href = child.getAttribute(HREF_ATTRIBUTE); if (href) { href = resolvePath(href, base); } src = child.getAttribute(SRC_ATTRIBUTE); if (src) { src = resolvePath(src, base); } const style = child.getAttribute(STYLE_ATTRIBUTE); if (style) { const declarations = replaceStylesheetUrls(resources, base, { data: style }, { context: DECLARATION_LIST_CONTEXT }, stylesheets, fetchMissingResources && missingResources, unfoundResources); if (!fetchMissingResources) { child.setAttribute(STYLE_ATTRIBUTE, declarations); } } const integrity = child.getAttribute(INTEGRITY_ATTRIBUTE); if (integrity) { child.removeAttribute(INTEGRITY_ATTRIBUTE); } } if (!enableScripts && child.removeAttribute) { EVENT_HANDLER_ATTRIBUTES.forEach(attribute => child.removeAttribute(attribute)); } if (child.tagName && child.tagName.toUpperCase() === LINK_TAG && href) { let rel = child.getAttribute(REL_ATTRIBUTE); if (rel) { rel = rel.toLowerCase(); if (rel === REL_ATTRIBUTE_STYLESHEET) { resource = getResource(resources, href, child.getAttribute(HREF_ATTRIBUTE)); if (resource) { let base = resource.id; if (base.startsWith(CID_PROTOCOL)) { if (index.match(CID_REGEXP)) { base = id; } else { base = index; } } const stylesheet = replaceStylesheetUrls(resources, base, resource, { context: STYLESHEET_CONTEXT }, stylesheets, fetchMissingResources && missingResources, unfoundResources); if (!fetchMissingResources) { const styleElement = document.createElement(STYLE_TAG); styleElement.type = STYLESHEET_CONTENT_TYPE; const media = child.getAttribute(MEDIA_ATTRIBUTE); if (media) { styleElement.setAttribute(MEDIA_ATTRIBUTE, media); } resource.used = true; resource.data = stylesheet; if (!href.startsWith(DATA_PROTOCOL)) { styleElement.setAttribute(ORIGINAL_URL_ATTRIBUTE_PREFIX + HREF_ATTRIBUTE, href); } styleElement.appendChild(document.createTextNode(resource.data)); child.replaceWith(styleElement); } } else if (fetchMissingResources) { addMissingResource(missingResources, href); } else { unfoundResources.add(href); setAttribute(child, HREF_ATTRIBUTE, href); } if (!fetchMissingResources) { const title = child.getAttribute(TITLE_ATTRIBUTE); if (title && rel.includes(REL_ATTRIBUTE_ALTERNATE)) { removedNodes.push(child); } } } else if (rel.includes(REL_ATTRIBUTE_ICON)) { resource = getResource(resources, href, child.getAttribute(HREF_ATTRIBUTE)); const media = child.getAttribute(MEDIA_ATTRIBUTE); const type = child.getAttribute(TYPE_ATTRIBUTE); const sizes = child.getAttribute(SIZES_ATTRIBUTE); if (resource) { if (!fetchMissingResources) { resource.used = true; const resourceURI = getResourceURI(resource); setAttribute(child, HREF_ATTRIBUTE, resourceURI); favicons.push({ href: resourceURI, media, type, sizes, originalHref: href }); } } else if (fetchMissingResources) { addMissingResource(missingResources, href, BASE64_ENCODING); } else { unfoundResources.add(href); setAttribute(child, HREF_ATTRIBUTE, href); favicons.push({ href, media, type, sizes }); } } else if (rel == REL_ATTRIBUTE_CANONICAL && !fetchMissingResources) { canonicalLinkElement = child; } if (!fetchMissingResources) { const relValue = rel .replace(REL_REMOVED_VALUES_REGEXP, EMPTY_STRING) .trim(); if (relValue.length) { child.setAttribute(REL_ATTRIBUTE, relValue); } else { removedNodes.push(child); } } } } else if (child.tagName && child.tagName.toUpperCase() === STYLE_TAG) { const style = replaceStylesheetUrls(resources, base, { data: child.textContent }, { context: STYLESHEET_CONTEXT }, stylesheets, fetchMissingResources && missingResources, unfoundResources); if (!fetchMissingResources) { const styleElement = document.createElement(STYLE_TAG); styleElement.type = STYLESHEET_CONTENT_TYPE; const media = child.getAttribute(MEDIA_ATTRIBUTE); if (media) { styleElement.setAttribute(MEDIA_ATTRIBUTE, media); } styleElement.appendChild(document.createTextNode(style)); child.replaceWith(styleElement); } } else if (child.tagName && child.tagName.toUpperCase() === IMG_TAG || child.tagName && child.tagName.toUpperCase() === AUDIO_TAG || child.tagName && child.tagName.toUpperCase() === VIDEO_TAG || child.tagName && child.tagName.toUpperCase() === SOURCE_TAG || child.tagName && child.tagName.toUpperCase() === SCRIPT_TAG) { if (src) { resource = getResource(resources, src, child.getAttribute(SRC_ATTRIBUTE)); if (resource) { if (!fetchMissingResources) { resource.used = true; setAttribute(child, SRC_ATTRIBUTE, getResourceURI(resource)); } } else if (fetchMissingResources) { addMissingResource(missingResources, src, BASE64_ENCODING); } else { unfoundResources.add(src); setAttribute(child, SRC_ATTRIBUTE, src); } } if (child.tagName && child.tagName.toUpperCase() === IMG_TAG || child.tagName && child.tagName.toUpperCase() === SOURCE_TAG) { const srcset = child.getAttribute(SRCSET_ATTRIBUTE); if (srcset) { const srcsetData = srcsetParser.parse(srcset).map(data => { const src = resolvePath(data.url, base); const resource = getResource(resources, src, data.url); if (resource) { if (!fetchMissingResources) { resource.used = true; data.url = getResourceURI(resource); } } else if (fetchMissingResources) { addMissingResource(missingResources, src, BASE64_ENCODING); } else { unfoundResources.add(src); data.url = src; } return data; }); if (!fetchMissingResources) { setAttribute(child, SRCSET_ATTRIBUTE, srcsetParser.serialize(srcsetData)); } } } else if (child.tagName && child.tagName.toUpperCase() === SCRIPT_TAG && !fetchMissingResources) { let type = child.getAttribute(TYPE_ATTRIBUTE); if (type) { type = type.toLowerCase(); } if (!enableScripts && (!type || type !== JSON_LD_CONTENT_TYPE)) { removedNodes.push(child); } } } else if (child.tagName && child.tagName.toUpperCase() === BODY_TAG || child.tagName && child.tagName.toUpperCase() === TABLE_TAG || child.tagName && child.tagName.toUpperCase() === TD_TAG || child.tagName && child.tagName.toUpperCase() === TH_TAG) { let background = child.getAttribute(BACKGROUND_ATTRIBUTE); if (background && !background.startsWith(DATA_PROTOCOL)) { background = resolvePath(background, base); resource = getResource(resources, background, child.getAttribute(BACKGROUND_ATTRIBUTE)); if (resource) { if (!fetchMissingResources) { resource.used = true; setAttribute(child, BACKGROUND_ATTRIBUTE, getResourceURI(resource)); } } else if (fetchMissingResources) { addMissingResource(missingResources, background, BASE64_ENCODING); } else { unfoundResources.add(background); setAttribute(child, BACKGROUND_ATTRIBUTE, background); } } } else if (child.tagName && child.tagName.toUpperCase() === INPUT_TAG) { const type = child.getAttribute(TYPE_ATTRIBUTE); if (type && type.toLowerCase() === TYPE_ATTRIBUTE_IMAGE && src) { resource = getResource(resources, src, child.getAttribute(SRC_ATTRIBUTE)); if (resource) { if (!fetchMissingResources) { resource.used = true; setAttribute(child, SRC_ATTRIBUTE, getResourceURI(resource)); } } else if (fetchMissingResources) { addMissingResource(missingResources, src, BASE64_ENCODING); } else { unfoundResources.add(src); setAttribute(child, SRC_ATTRIBUTE, src); } } } else if (child.tagName && child.tagName.toUpperCase() === IFRAME_TAG || child.tagName && child.tagName.toUpperCase() === FRAME_TAG || child.tagName && child.tagName.toUpperCase() === EMBED_TAG || child.tagName && child.tagName.toUpperCase() === OBJECT_TAG) { let id, attribute; if (child.tagName && child.tagName.toUpperCase() === OBJECT_TAG) { attribute = DATA_ATTRIBUTE; src = child.getAttribute(DATA_ATTRIBUTE); if (src) { src = resolvePath(src, base); } } else { attribute = SRC_ATTRIBUTE; } if (src) { if (src.startsWith(CID_PROTOCOL)) { id = `<${src.split(CID_PROTOCOL)[1]}>`; resource = frames[id]; } else { id = src; resource = getResource(resources, src, child.getAttribute(attribute)); } if (resource) { if (child.tagName && child.tagName.toUpperCase() === EMBED_TAG || child.tagName && child.tagName.toUpperCase() === OBJECT_TAG) { if (!fetchMissingResources) { resource.used = true; setAttribute(child, attribute, getResourceURI(resource)); } } else { const result = convert({ resources: Object.assign({}, resources, { [id]: resource }), unfoundResources, frames: frames, index: id, id: resource.id }, { DOMParser, enableScripts, fetchMissingResources }); if (fetchMissingResources) { for (const missingResource of result) { if (!missingResources.find(resource => resource.id === missingResource.id)) { missingResources.push(missingResource); } } } else { resource.used = true; if (child.tagName && child.tagName.toUpperCase() === IFRAME_TAG) { setAttribute(child, SRC_ATTRIBUTE); child.removeAttribute(SRC_ATTRIBUTE); child.setAttribute(SRCDOC_ATTRIBUTE, result.data); } else { setAttribute(child, attribute, DATA_PROTOCOL + TEXT_HTML_CONTENT_TYPE + DATA_URI_PAYLOAD_SEPARATOR + encodeURIComponent(result.data)); } } } } else if (fetchMissingResources) { addMissingResource(missingResources, src); } else { unfoundResources.add(src); setAttribute(child, attribute, src); } } } else if ((child.tagName && child.tagName.toUpperCase() === A_TAG || child.tagName && child.tagName.toUpperCase() === AREA_TAG) && !fetchMissingResources) { if (href) { try { const url = new URL(child.getAttribute(HREF_ATTRIBUTE), base); const hash = url.hash; url.hash = EMPTY_STRING; if (url == base && hash) { child.setAttribute(HREF_ATTRIBUTE, hash); } else { child.setAttribute(HREF_ATTRIBUTE, href); } // eslint-disable-next-line no-unused-vars } catch (_) { child.setAttribute(HREF_ATTRIBUTE, href); } } child.removeAttribute(PING_ATTRIBUTE); } else if (child.tagName && child.tagName.toUpperCase() === META_TAG && !fetchMissingResources) { let httpEquiv = child.getAttribute(HTTP_EQUIV_ATTRIBUTE); if (httpEquiv) { httpEquiv = httpEquiv.toLowerCase(); if (httpEquiv === HTTP_EQUIV_ATTRIBUTE_REFRESH || httpEquiv === HTTP_EQUIV_ATTRIBUTE_CSP) { removedNodes.push(child); } } } else if (child.tagName && child.tagName.toUpperCase() === TEMPLATE_TAG && !fetchMissingResources) { const shadowModeAttribute = child.getAttribute(SHADOWMODE_ATTRIBUTE); if (shadowModeAttribute) { child.removeAttribute(SHADOWMODE_ATTRIBUTE); child.setAttribute(SHADOWROOTMODE_ATTRIBUTE, shadowModeAttribute); } if (child.content) { child.content.childNodes.forEach(node => nodes.push(node)); } } else if (child.tagName && child.tagName.toUpperCase() === TITLE_TAG && !fetchMissingResources && childNode.tagName && childNode.tagName.toUpperCase() === HEAD_TAG && title === undefined && child.textContent) { title = child.textContent; } nodes.push(child); } } } if (fetchMissingResources) { return missingResources; } else { removedNodes.forEach(node => node.remove()); if (!canonicalLinkElement) { const linkElement = document.createElement(LINK_TAG); linkElement.setAttribute(REL_ATTRIBUTE, REL_ATTRIBUTE_CANONICAL); linkElement.setAttribute(HREF_ATTRIBUTE, index); document.head.appendChild(linkElement); } let metaElement = document.createElement(META_TAG); metaElement.setAttribute(HTTP_EQUIV_ATTRIBUTE, HTTP_EQUIV_ATTRIBUTE_CSP); let csp = DEFAULT_CSP; if (enableScripts) { csp += JS_ENABLED_CSP; } else { csp += JS_DISABLED_CSP; } metaElement.setAttribute(CONTENT_ATTRIBUTE, csp); if (document.head.firstChild) { document.head.prepend(metaElement); } else { document.head.appendChild(metaElement); } metaElement.setAttribute(CONTENT_ATTRIBUTE, csp); metaElement = document.createElement(META_TAG); metaElement.setAttribute(CHARSET_ATTRIBUTE, UTF8_CHARSET); document.head.prepend(metaElement); if (headers) { const pageInfoElement = document.createElement(SCRIPT_TAG); pageInfoElement.setAttribute(TYPE_ATTRIBUTE, JSON_LD_CONTENT_TYPE); pageInfoElement.appendChild(document.createTextNode(JSON.stringify(getPageInfo(headers, index), null, 2))); if (document.head.firstChild) { document.head.firstChild.after(pageInfoElement); } else { document.head.appendChild(pageInfoElement); } } if (unfoundResources.size) { unfoundResources.forEach(id => { if (!id.startsWith(DATA_PROTOCOL)) { resources[id] = { id, notFound: true, used: true }; } }); } return { title, favicons, data: dom.serialize() }; } } function setAttribute(element, attribute, newValue) { const value = element.getAttribute(attribute); if (value && !value.startsWith(DATA_PROTOCOL) && value !== newValue) { element.setAttribute(ORIGINAL_URL_ATTRIBUTE_PREFIX + attribute, value); } if (newValue !== undefined) { element.setAttribute(attribute, newValue); } } function replaceStylesheetUrls(resources, base, resource, options = {}, stylesheets, missingResources, unfoundResources) { let ast; if (resource.id !== undefined) { if (stylesheets[resource.id]) { return stylesheets[resource.id].data; } else { stylesheets[resource.id] = {}; } } try { ast = cssTree.parse(resource.data, options); // eslint-disable-next-line no-unused-vars } catch (_) { // ignored } if (ast) { cssTree.walk(ast, node => { if (node.type === URL_FUNCTION) { const path = node.value; if (!path.startsWith(DATA_PROTOCOL) && !path.startsWith(ORIGINAL_URL_FUNCTION_NAME)) { const id = resolvePath(path, base); const resource = getResource(resources, id, path); if (resource) { if (!missingResources) { resource.used = true; if (isStylesheet(resource.contentType)) { resource.data = replaceStylesheetUrls(resources, resource.id, resource, { context: STYLESHEET_CONTEXT }, stylesheets, missingResources, unfoundResources); } node.value = getOriginalUrlFunction(id, getResourceURI(resource)); } } else if (missingResources) { addMissingResource(missingResources, id, BASE64_ENCODING); } else { unfoundResources.add(id); node.value = getOriginalUrlFunction(id); } } } else if (node.type === AT_RULE && node.name.toLowerCase() === IMPORT_RULE) { const path = node.prelude.children.first.value; if (!path.startsWith(DATA_PROTOCOL) && !path.startsWith(ORIGINAL_URL_FUNCTION_NAME)) { const id = resolvePath(path, base); const resource = getResource(resources, id, path); if (resource) { resource.data = replaceStylesheetUrls(resources, resource.id, resource, { context: STYLESHEET_CONTEXT }, stylesheets, missingResources, unfoundResources); if (!missingResources) { resource.used = true; node.prelude.children.first.value = getOriginalUrlFunction(id, getResourceURI(resource)); } } else if (missingResources) { addMissingResource(missingResources, id); } else { unfoundResources.add(id); node.prelude.children.first.value = getOriginalUrlFunction(id); } } } }); try { const result = cssTree.generate(ast); if (resource.id !== undefined) { stylesheets[resource.id].data = result; } return result.replace(ORIGINAL_URL_FUNCTION_REGEXP, ORIGINAL_URL_COMMENT); // eslint-disable-next-line no-unused-vars } catch (_) { return resource.data; } } else { return resource.data; } } function getResource(resources, id, rawId) { let resource = resources[id]; if (!resource) { resource = resources[rawId]; } return resource; } function addMissingResource(missingResources, id, transferEncoding) { if ((id.startsWith(HTTP_PROTOCOL) || id.startsWith(HTTPS_PROTOCOL) || id.startsWith(URN_PROTOCOL)) && !missingResources.find(resource => resource.id === id)) { missingResources.push({ id, transferEncoding }); } } function getOriginalUrlFunction(id, resourceURI = id) { return ORIGINAL_URL_FUNCTION_NAME + CSS_FUNCTION_PARENTHESIS_START + JSON.stringify(id) + CSS_FUNCTION_PARENTHESIS_END + resourceURI; } function getPageInfo(headers, index) { return { ...JSON_LD_PAGE_INFO, url: index, name: decodeMimeHeader(headers[SUBJECT_HEADER]), dateCreated: headers[DATE_HEADER], additionalProperty: { ...JSON_LD_PAGE_INFO.additionalProperty, value: decodeMimeHeader(headers[FROM_HEADER]) } }; }