import { SITE } from './consts'; export const personId = `${SITE.url}/#person`; export const websiteId = `${SITE.url}/#website`; // Canonical Person entity — referenced by @id from every other entity. export const person = { '@type': 'Person', '@id': personId, name: 'Asger Geel Weirsøe', alternateName: 'Asger Weirsøe', url: `${SITE.url}/`, image: `${SITE.url}/asger.png`, jobTitle: ['Datalog', 'Technical Solution Architect'], hasOccupation: { '@type': 'Occupation', name: 'Datalog', occupationalCategory: '2512 Softwareudvikler', }, hasCredential: [ { '@type': 'EducationalOccupationalCredential', credentialCategory: 'degree', educationalLevel: "Master's degree", name: 'Cand.scient. i Datalogi', recognizedBy: { '@type': 'CollegeOrUniversity', name: 'Aalborg Universitet' }, }, { '@type': 'EducationalOccupationalCredential', credentialCategory: 'license', name: 'Radioamatørlicens, kategori B (kaldesignal OZ0AGW)', dateCreated: '2024-05-18', }, ], // schema.org/callSign — kaldesignalet er en entydig identifikator på linje med // sameAs-profilerne og knytter OZ0AGW til personen. callSign: 'OZ0AGW', worksFor: { '@type': 'Organization', name: 'Digisense A/S' }, alumniOf: { '@type': 'CollegeOrUniversity', name: 'Aalborg Universitet' }, homeLocation: { '@type': 'Place', name: 'Nørresundby, Danmark' }, nationality: { '@type': 'Country', name: 'Danmark' }, knowsLanguage: ['da', 'en'], knowsAbout: [ 'Peppol', 'Peppol Access Point', 'Peppol BIS', 'Peppol eDelivery', 'Oxalis', 'NemHandel', 'Elektronisk fakturering', 'E-faktura', 'Electronic invoicing', 'OIOUBL', 'UBL', 'e-Delivery (AS4)', 'EN 16931', 'E-procurement', 'EDI', 'Softwarearkitektur', 'Løsningsarkitektur', 'Forretningsforståelse', 'Digitalisering af fakturaflows', 'Rådgivning', 'Kravspecifikation', 'Proxmox', 'Selvhosting', 'Kunstig intelligens', 'Linux', 'DevOps', 'OIDC', 'SSO', 'Fuld-stack udvikling', 'Docker', 'Amatørradio', 'Radioamatør', 'Radiokommunikation', ], sameAs: [ 'https://www.linkedin.com/in/ag-weirs%C3%B8e/', 'https://mastodon.social/@asgerweirsoee', 'https://github.com/asger-weirsoee', 'https://gitea.weircon.dk/agw', 'https://arxiv.org/abs/2310.00999', ], description: 'Asger Weirsøe er datalog (cand.scient. fra Aalborg Universitet) og Technical Solution Architect hos Digisense A/S. Han bygger bro mellem forretning og teknik og har en bred profil inden for elektronisk fakturering og e-procurement: Peppol (Access Point, eDelivery/AS4, Oxalis), NemHandel og OIOUBL, kombineret med forretningsforståelse, selvhosting, Proxmox og AI. Han er desuden licenseret radioamatør med B-licens og kaldesignalet OZ0AGW.', }; export const website = { '@type': 'WebSite', '@id': websiteId, url: `${SITE.url}/`, name: SITE.name, description: SITE.description, inLanguage: 'da-DK', publisher: { '@id': personId }, }; // Default graph present on every page (Google de-dupes by @id). export const baseGraph = { '@context': 'https://schema.org', '@graph': [website, person] }; // Marks the front page as a ProfilePage about the Person — a strong "this site is // about this human" signal that helps Google build a knowledge-panel entity. export const profilePage = { '@context': 'https://schema.org', '@type': 'ProfilePage', '@id': `${SITE.url}/#profilepage`, url: `${SITE.url}/`, name: 'Asger Weirsøe', mainEntity: { '@id': personId }, about: { '@id': personId }, inLanguage: 'da-DK', }; // FAQ pairs — natural-language Q&A that search engines and LLMs can lift verbatim. export function faqPage(items: { q: string; a: string }[]) { return { '@context': 'https://schema.org', '@type': 'FAQPage', inLanguage: 'da-DK', mainEntity: items.map((it) => ({ '@type': 'Question', name: it.q, acceptedAnswer: { '@type': 'Answer', text: it.a }, })), }; } export function blogPosting(opts: { url: string; headline: string; description: string; datePublished: string; dateModified: string; tags: string[]; image: string; wordCount?: number; section?: string; }) { return { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: opts.headline, description: opts.description, url: opts.url, mainEntityOfPage: opts.url, datePublished: opts.datePublished, dateModified: opts.dateModified, inLanguage: 'da-DK', image: opts.image, keywords: opts.tags.join(', '), articleSection: opts.section || 'Blog', ...(opts.wordCount ? { wordCount: opts.wordCount } : {}), author: { '@id': personId }, publisher: { '@id': personId }, isPartOf: { '@id': websiteId }, }; } // BreadcrumbList — the highest-ROI rich result for content sites: a human-readable // path (Forside › Blog › …) in the SERP instead of the raw URL. Needs ≥2 items. export function breadcrumb(items: { name: string; url: string }[]) { return { '@context': 'https://schema.org', '@type': 'BreadcrumbList', itemListElement: items.map((it, i) => ({ '@type': 'ListItem', position: i + 1, name: it.name, item: it.url, })), }; } // CollectionPage — tells search/AI that a listing page (the blog index, a tag) is a // curated set authored by the Person, not a thin/duplicate page. export function collectionPage(opts: { url: string; name: string; description: string }) { return { '@context': 'https://schema.org', '@type': 'CollectionPage', '@id': opts.url, url: opts.url, name: opts.name, description: opts.description, inLanguage: 'da-DK', isPartOf: { '@id': websiteId }, about: { '@id': personId }, }; }