import { getCollection } from "astro:content"; const siteUrl = ( import.meta.env.SITE_URL || import.meta.env.PUBLIC_SITE_URL || "https://quietpages-eta.vercel.app" ).replace(/\/$/, ""); export const authors = [ { slug: "carlos-arias", name: "Carlos Arias", bio: "AI engineer and digital strategist with 25+ years building software and AI systems; founder of CarlosArias&Co and engineer behind Medellín.co.", longBio: "Carlos Arias is an AI engineer and strategist with over two decades across software engineering, digital marketing, and AI. He is the founder of CarlosArias&Co and Snoopi.io, and an engineer and strategist behind the Medellín.co city guide. Based between South Florida and Latin America, he builds AI-driven systems for local visibility and revenue-tracked growth. Comiida's articles are researched from public sources and AI-assisted under his editorial oversight.", avatar: "https://carlosarias.com/wp-content/uploads/2023/02/cropped-clos.jpg", jobTitle: "Founder & Editor, Comiida", knowsAbout: [ "Medellín restaurants", "Colombian cuisine", "dining guides", "local SEO", ], sameAs: [ "https://x.com/hicarlosarias", "https://www.linkedin.com/in/hicarlosarias/", "https://www.instagram.com/carlosarias.co", "https://www.facebook.com/hicarlosarias", "https://carlosarias.com", ], }, ]; export const categories = [ { slug: "guides", name: "Guides" }, { slug: "news", name: "News" }, { slug: "reviews", name: "Reviews" }, { slug: "neighborhoods", name: "Neighborhoods" }, ]; export const tags = [ { slug: "food-guide", name: "Food Guide" }, { slug: "ciudad-del-rio", name: "Ciudad Del Rio" }, { slug: "things-to-do", name: "Things To Do" }, { slug: "terminal-del-sur", name: "Terminal Del Sur" }, { slug: "specialty-coffee", name: "Specialty Coffee" }, { slug: "craft-beer", name: "Craft Beer" }, { slug: "rionegro", name: "Rionegro" }, { slug: "antioquia", name: "Antioquia" }, { slug: "day-trip", name: "Day Trip" }, { slug: "oriente-antioqueno", name: "Oriente Antioqueno" }, { slug: "food-festival", name: "Food Festival" }, { slug: "marinilla", name: "Marinilla" }, { slug: "wake-medellin", name: "Wake Medellin" }, { slug: "peruvian-cuisine", name: "Peruvian Cuisine" }, { slug: "steakhouse", name: "Steakhouse" }, { slug: "gastronomy", name: "Gastronomy" }, { slug: "time-out", name: "Time Out" }, { slug: "food-ranking", name: "Food Ranking" }, { slug: "chef", name: "Chef" }, { slug: "colombian-cuisine", name: "Colombian Cuisine" }, { slug: "new-restaurant", name: "New Restaurant" }, { slug: "legal-news", name: "Legal News" }, { slug: "fine-dining", name: "Fine Dining" }, { slug: "hidden-kitchen", name: "Hidden Kitchen" }, { slug: "chefs-table", name: "Chefs Table" }, { slug: "tasting-menu", name: "Tasting Menu" }, { slug: "new-openings", name: "New Openings" }, { slug: "restaurant-news", name: "Restaurant News" }, { slug: "restaurants", name: "Restaurants" }, { slug: "coffee", name: "Coffee" }, { slug: "el-poblado", name: "El Poblado" }, { slug: "brunch", name: "Brunch" }, { slug: "expats", name: "Expats" }, { slug: "food-safety", name: "Food Safety" }, { slug: "street-food", name: "Street Food" }, { slug: "medellin", name: "Medellín" }, { slug: "travel", name: "Travel" }, ]; export const imageSrc = (image) => (typeof image === "string" ? image : image?.src); // Keep the FULL ISO timestamp (not date-only) so posts published on the same day order by // their real publish time, and so Article schema / sitemap / RSS get precise datePublished. export const normalizePost = (entry) => ({ slug: entry.id, ...entry.data, date: entry.data.date?.toISOString(), updated: entry.data.updated?.toISOString(), }); export const posts = async () => (await getCollection("blog")).map(normalizePost); export const getPost = async (slug) => (await posts()).find((post) => post.slug === slug); export const getAuthor = (slug) => authors.find((author) => author.slug === slug); export const getCategory = (slug) => categories.find((category) => category.slug === slug); export const getTag = (slug) => tags.find((tag) => tag.slug === slug); export const postsByCategory = async (slug) => (await sortedPosts()).filter((post) => post.category === slug); export const postsByTag = async (slug) => (await sortedPosts()).filter((post) => post.tags.includes(slug)); export const postsByAuthor = async (slug) => (await sortedPosts()).filter((post) => post.author === slug); export const sortedPosts = async () => [...(await posts())].sort((a, b) => { const diff = new Date(b.date) - new Date(a.date); // newest first return diff !== 0 ? diff : a.slug.localeCompare(b.slug); // stable tiebreaker }); export const featuredPost = async () => { const sorted = await sortedPosts(); return sorted.find((post) => post.featured) ?? sorted[0]; }; export const popularPosts = async () => (await sortedPosts()).slice(0, 4); export const relatedPosts = async (post, n = 3) => (await sortedPosts()) .filter((candidate) => candidate.slug !== post.slug) .sort((a, b) => { const score = (candidate) => (candidate.category === post.category ? 2 : 0) + candidate.tags.filter((tag) => post.tags.includes(tag)).length; return score(b) - score(a); }) .slice(0, n); export const adjacentPosts = async (post) => { const sorted = await sortedPosts(); const index = sorted.findIndex((candidate) => candidate.slug === post.slug); return { prev: sorted[index + 1], next: sorted[index - 1] }; }; export const formatDate = (iso) => new Date(iso).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }); export const SITE = { name: "Comiida", description: "Medellín's restaurant scene for English-speaking expats, nomads, and travelers — guides, news, and data-driven dining picks.", url: siteUrl, }; // ---- JSON-LD schema builders (no @context — nest these, or add @context at the top level) ---- export const organizationSchema = () => ({ "@type": "Organization", "@id": `${SITE.url}/#organization`, name: SITE.name, url: `${SITE.url}/`, description: SITE.description, logo: `${SITE.url}/favicon.svg`, }); export const websiteSchema = () => ({ "@type": "WebSite", "@id": `${SITE.url}/#website`, name: SITE.name, url: `${SITE.url}/`, description: SITE.description, inLanguage: "en", publisher: { "@id": `${SITE.url}/#organization` }, }); export const personSchema = (author) => author && { "@type": "Person", "@id": `${SITE.url}/authors/${author.slug}#person`, name: author.name, url: `${SITE.url}/authors/${author.slug}`, image: author.avatar, description: author.longBio || author.bio, ...(author.jobTitle ? { jobTitle: author.jobTitle } : {}), ...(author.knowsAbout ? { knowsAbout: author.knowsAbout } : {}), ...(author.sameAs ? { sameAs: author.sameAs } : {}), };