1import { getMetadata } from './aem.js'; 2 3const SITE_URL = 'https://www.recordedfuture.com'; 4const ORG_ID = `${SITE_URL}#organization`; 5const WEBSITE_ID = `${SITE_URL}#website`; 6const ORG_NAME = 'Recorded Future'; 7const LOGO_URL = 'https://www.recordedfuture.com/media_1f4421a86fb70701080e681db00fb61c46faf2612.png'; 8const SAME_AS = [ 9 'https://www.linkedin.com/company/recorded-future', 10 'https://x.com/RecordedFuture', 11 'https://www.youtube.com/@RecordedFuture', 12]; 13 14export function toISODate(input) { 15 if (!input) return ''; 16 let date; 17 if (/^\d+$/.test(input)) { 18 const ts = Number(input); 19 date = new Date(ts < 1e12 ? ts * 1000 : ts); 20 } else { 21 date = new Date(input); 22 } 23 if (Number.isNaN(date.getTime())) return ''; 24 return date.toISOString().split('T')[0]; 25} 26 27export function injectSchema(obj) { 28 if (!obj) return; 29 const script = document.createElement('script'); 30 script.type = 'application/ld+json'; 31 script.textContent = JSON.stringify(obj); 32 document.head.appendChild(script); 33} 34 35export function buildOrganizationSchema() { 36 return { 37 '@context': 'https://schema.org', 38 '@type': 'Organization', 39 '@id': ORG_ID, 40 name: ORG_NAME, 41 url: SITE_URL, 42 logo: { 43 '@type': 'ImageObject', 44 '@id': `${SITE_URL}#logo`, 45 url: LOGO_URL, 46 }, 47 sameAs: SAME_AS, 48 }; 49} 50 51export function buildWebSiteSchema() { 52 return { 53 '@context': 'https://schema.org', 54 '@type': 'WebSite', 55 '@id': WEBSITE_ID, 56 name: ORG_NAME, 57 url: SITE_URL, 58 publisher: { '@id': ORG_ID }, 59 }; 60} 61 62const BLOG_TYPE = 'blog'; 63 64export function buildArticleSchema() { 65 const canonical = 66 document.querySelector('link[rel="canonical"]')?.href || `${window.location.origin}${window.location.pathname}`; 67 const resourceType = getMetadata('resource-type').toLowerCase(); 68 const schemaType = resourceType === BLOG_TYPE ? 'BlogPosting' : 'Article'; 69 const headline = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 70 const description = getMetadata('og:description') || ''; 71 const imageUrl = getMetadata('og:image') || document.querySelector('picture img')?.src || ''; 72 const authorName = getMetadata('author'); 73 const datePublished = toISODate(getMetadata('published-date')); 74 const dateModified = toISODate(document.lastModified); 75 const topicTags = getMetadata('topic-tags') 76 .split(',') 77 .map((t) => t.trim()) 78 .filter(Boolean); 79 80 const schema = { 81 '@context': 'https://schema.org', 82 '@type': schemaType, 83 headline, 84 description, 85 image: { '@type': 'ImageObject', url: imageUrl }, 86 publisher: { 87 '@id': ORG_ID, 88 '@type': 'Organization', 89 name: ORG_NAME, 90 logo: { 91 '@type': 'ImageObject', 92 '@id': `${SITE_URL}#logo`, 93 url: LOGO_URL, 94 }, 95 }, 96 mainEntityOfPage: { '@id': `${canonical}#webpage` }, 97 }; 98 99 if (authorName) schema.author = { '@type': 'Person', name: authorName }; 100 if (datePublished) schema.datePublished = datePublished; 101 if (dateModified) schema.dateModified = dateModified; 102 if (topicTags.length) schema.keywords = topicTags; 103 104 return schema; 105} 106 107export function buildResearchSchema() { 108 const canonical = 109 document.querySelector('link[rel="canonical"]')?.href || `${window.location.origin}${window.location.pathname}`; 110 const headline = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 111 const description = getMetadata('og:description') || ''; 112 const imageUrl = getMetadata('og:image') || document.querySelector('picture img')?.src || ''; 113 const authorName = getMetadata('author'); 114 const datePublished = toISODate(getMetadata('published-date')); 115 const dateModified = toISODate(document.lastModified); 116 const topicTags = getMetadata('topic-tags') 117 .split(',') 118 .map((t) => t.trim()) 119 .filter(Boolean); 120 121 const schema = { 122 '@context': 'https://schema.org', 123 '@type': 'ScholarlyArticle', 124 headline, 125 description, 126 image: { '@type': 'ImageObject', url: imageUrl }, 127 publisher: { '@id': ORG_ID }, 128 mainEntityOfPage: { '@id': `${canonical}#webpage` }, 129 }; 130 131 if (authorName) schema.author = { '@type': 'Person', name: authorName }; 132 if (datePublished) schema.datePublished = datePublished; 133 if (dateModified) schema.dateModified = dateModified; 134 if (topicTags.length) schema.keywords = topicTags; 135 136 return schema; 137} 138 139export function buildCaseStudySchema() { 140 const canonical = 141 document.querySelector('link[rel="canonical"]')?.href || `${window.location.origin}${window.location.pathname}`;
142 const headline = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 143 const description = getMetadata('og:description') || ''; 144 const imageUrl = getMetadata('og:image') || document.querySelector('picture img')?.src || ''; 145 const datePublished = toISODate(getMetadata('published-date')); 146 const dateModified = toISODate(document.lastModified); 147 const topicTags = getMetadata('topic-tags') 148 .split(',') 149 .map((t) => t.trim()) 150 .filter(Boolean); 151 152 const schema = { 153 '@context': 'https://schema.org', 154 '@type': 'Article', 155 headline, 156 description, 157 image: { '@type': 'ImageObject', url: imageUrl }, 158 publisher: { '@id': ORG_ID }, 159 mainEntityOfPage: { '@id': `${canonical}#webpage` }, 160 }; 161 162 if (datePublished) schema.datePublished = datePublished; 163 if (dateModified) schema.dateModified = dateModified; 164 if (topicTags.length) schema.keywords = topicTags; 165 166 return schema; 167} 168 169export function buildIntegrationsSchema() { 170 const name = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 171 const description = getMetadata('og:description') || ''; 172 173 return { 174 '@context': 'https://schema.org', 175 '@type': 'SoftwareApplication', 176 name, 177 description, 178 applicationCategory: 'SecurityApplication', 179 operatingSystem: 'All', 180 provider: { '@id': ORG_ID }, 181 }; 182} 183 184export function buildServiceSchema() { 185 if (getMetadata('schema').toLowerCase() !== 'service') return null; 186 const canonical = 187 document.querySelector('link[rel="canonical"]')?.href || `${window.location.origin}${window.location.pathname}`; 188 const name = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 189 const description = getMetadata('og:description') || ''; 190 191 return { 192 '@context': 'https://schema.org', 193 '@type': 'Service', 194 name, 195 description, 196 url: canonical, 197 provider: { '@id': ORG_ID }, 198 }; 199} 200 201export function buildServiceBreadcrumbSchema() { 202 if (getMetadata('schema').toLowerCase() !== 'service') return null; 203 return buildBreadcrumbSchema('Services', '/services'); 204} 205 206export function buildProductSchema() { 207 if (getMetadata('schema').toLowerCase() !== 'product') return null; 208 const canonical = 209 document.querySelector('link[rel="canonical"]')?.href || `${window.location.origin}${window.location.pathname}`; 210 const name = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 211 const description = getMetadata('og:description') || ''; 212 const imageUrl = getMetadata('og:image') || document.querySelector('picture img')?.src || LOGO_URL; 213 214 return { 215 '@context': 'https://schema.org', 216 '@type': 'Product', 217 name, 218 description, 219 image: { '@type': 'ImageObject', url: imageUrl }, 220 url: canonical, 221 provider: { '@id': ORG_ID }, 222 }; 223} 224 225export function buildCveSchema() { 226 const canonical = 227 document.querySelector('link[rel="canonical"]')?.href || `${window.location.origin}${window.location.pathname}`; 228 const headline = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 229 const description = getMetadata('og:description') || ''; 230 231 return { 232 '@context': 'https://schema.org', 233 '@type': 'TechArticle', 234 headline, 235 description, 236 publisher: { '@id': ORG_ID }, 237 mainEntityOfPage: { '@id': `${canonical}#webpage` }, 238 }; 239} 240 241export function buildWebPageSchema() { 242 const canonical = document.querySelector('link[rel="canonical"]')?.href || window.location.href; 243 const name = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 244 const description = getMetadata('og:description') || getMetadata('description') || ''; 245 const dateModified = toISODate(document.lastModified); 246 const topicTags = getMetadata('topic-tags') 247 .split(',') 248 .map((t) => t.trim()) 249 .filter(Boolean); 250 251 const schema = { 252 '@context': 'https://schema.org', 253 '@type': 'WebPage', 254 '@id': `${canonical}#webpage`, 255 name, 256 description, 257 url: canonical, 258 isPartOf: { '@id': WEBSITE_ID }, 259 about: { '@id': ORG_ID }, 260 }; 261 262 if (dateModified) schema.dateModified = dateModified; 263 if (topicTags.length) schema.keywords = topicTags; 264 return schema; 265} 266 267export function buildBreadcrumbSchema(parentLabel = '', parentUrl = '') { 268 const canonical = 269 document.querySelector('link[rel="canonical"]')?.href || `${window.location.origin}${window.location.pathname}`;
270 const name = getMetadata('og:title') || document.querySelector('h1')?.textContent?.trim() || ''; 271 const absoluteParentUrl = parentUrl.startsWith('/') ? `${SITE_URL}${parentUrl}` : parentUrl; 272 273 const items = [{ '@type': 'ListItem', position: 1, name: 'Home', item: SITE_URL }]; 274 275 if (parentLabel && parentUrl) { 276 items.push({ '@type': 'ListItem', position: 2, name: parentLabel, item: absoluteParentUrl }); 277 } 278 279 items.push({ '@type': 'ListItem', position: items.length + 1, name, item: canonical }); 280 281 return { 282 '@context': 'https://schema.org', 283 '@type': 'BreadcrumbList', 284 itemListElement: items, 285 }; 286}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.