vaza-content 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/astro/index.cjs +75 -0
- package/dist/adapters/astro/index.cjs.map +1 -0
- package/dist/adapters/astro/index.d.cts +23 -0
- package/dist/adapters/astro/index.d.ts +23 -0
- package/dist/adapters/astro/index.js +75 -0
- package/dist/adapters/astro/index.js.map +1 -0
- package/dist/adapters/next/index.cjs +82 -0
- package/dist/adapters/next/index.cjs.map +1 -0
- package/dist/adapters/next/index.d.cts +17 -0
- package/dist/adapters/next/index.d.ts +17 -0
- package/dist/adapters/next/index.js +82 -0
- package/dist/adapters/next/index.js.map +1 -0
- package/dist/adapters/nuxt/index.cjs +70 -0
- package/dist/adapters/nuxt/index.cjs.map +1 -0
- package/dist/adapters/nuxt/index.d.cts +18 -0
- package/dist/adapters/nuxt/index.d.ts +18 -0
- package/dist/adapters/nuxt/index.js +70 -0
- package/dist/adapters/nuxt/index.js.map +1 -0
- package/dist/adapters/sveltekit/index.cjs +78 -0
- package/dist/adapters/sveltekit/index.cjs.map +1 -0
- package/dist/adapters/sveltekit/index.d.cts +19 -0
- package/dist/adapters/sveltekit/index.d.ts +19 -0
- package/dist/adapters/sveltekit/index.js +78 -0
- package/dist/adapters/sveltekit/index.js.map +1 -0
- package/dist/blog-7EEJJG26.js +106 -0
- package/dist/blog-7EEJJG26.js.map +1 -0
- package/dist/blog-Z3R5GOMP.cjs +106 -0
- package/dist/blog-Z3R5GOMP.cjs.map +1 -0
- package/dist/chunk-7VCRDESM.cjs +1050 -0
- package/dist/chunk-7VCRDESM.cjs.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-JEQ2X3Z6.cjs +11 -0
- package/dist/chunk-JEQ2X3Z6.cjs.map +1 -0
- package/dist/chunk-L4VH2NSA.js +1050 -0
- package/dist/chunk-L4VH2NSA.js.map +1 -0
- package/dist/cli/index.cjs +256 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +256 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/dark-6E36AKLN.js +70 -0
- package/dist/dark-6E36AKLN.js.map +1 -0
- package/dist/dark-EX2GRAYK.cjs +70 -0
- package/dist/dark-EX2GRAYK.cjs.map +1 -0
- package/dist/index.cjs +97 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +70 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +1 -0
- package/dist/minimal-D2PRAVG6.js +54 -0
- package/dist/minimal-D2PRAVG6.js.map +1 -0
- package/dist/minimal-RHOK4XEZ.cjs +54 -0
- package/dist/minimal-RHOK4XEZ.cjs.map +1 -0
- package/dist/process-FYZZ5QAG.js +8 -0
- package/dist/process-FYZZ5QAG.js.map +1 -0
- package/dist/process-XEGGGY6S.cjs +8 -0
- package/dist/process-XEGGGY6S.cjs.map +1 -0
- package/dist/product-NCUW3U72.js +97 -0
- package/dist/product-NCUW3U72.js.map +1 -0
- package/dist/product-OT3XYMWD.cjs +97 -0
- package/dist/product-OT3XYMWD.cjs.map +1 -0
- package/dist/types-Bkue7DeN.d.cts +247 -0
- package/dist/types-Bkue7DeN.d.ts +247 -0
- package/package.json +94 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/mehdi/vaza-content/dist/chunk-7VCRDESM.cjs","../src/normalize/blur-placeholder.ts","../src/normalize/excerpt.ts","../src/normalize/description.ts","../src/normalize/image-dimensions.ts","../src/normalize/word-count.ts","../src/normalize/reading-time.ts","../src/normalize/slug.ts","../src/normalize/toc.ts","../src/normalize/index.ts","../src/generate/sitemap.ts","../src/generate/rss.ts","../src/generate/json-ld/article.ts","../src/generate/json-ld/faq.ts","../src/generate/breadcrumbs.ts","../src/generate/json-ld/breadcrumb.ts","../src/generate/json-ld/how-to.ts","../src/generate/json-ld/product.ts","../src/generate/json-ld/recipe.ts","../src/generate/json-ld/event.ts","../src/generate/json-ld/index.ts","../src/generate/og-images.ts","../src/generate/taxonomy.ts","../src/generate/redirects.ts","../src/integrity/alt-text.ts","../src/integrity/broken-links.ts","../src/integrity/duplicate-content.ts","../src/integrity/duplicate-slugs.ts","../src/integrity/freshness.ts","../src/integrity/meta-length.ts","../src/integrity/orphan-pages.ts","../src/integrity/index.ts","../src/process.ts"],"names":[],"mappings":"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","file":"/Users/mehdi/vaza-content/dist/chunk-7VCRDESM.cjs","sourcesContent":[null,"import sharp from \"sharp\";\n\n/**\n * Generate a tiny blurred placeholder image and return it as a base64 data URL.\n * Returns undefined if the image cannot be read.\n */\nexport async function generateBlurPlaceholder(\n imagePath: string,\n): Promise<string | undefined> {\n try {\n const buffer = await sharp(imagePath)\n .resize(8, 8, { fit: \"inside\" })\n .blur()\n .png()\n .toBuffer();\n\n return `data:image/png;base64,${buffer.toString(\"base64\")}`;\n } catch {\n return undefined;\n }\n}\n","/**\n * Strip common markdown syntax from text.\n */\nfunction stripMarkdown(text: string): string {\n return (\n text\n // Remove images \n .replace(/!\\[.*?\\]\\(.*?\\)/g, \"\")\n // Remove links but keep text [text](url)\n .replace(/\\[([^\\]]*)\\]\\(.*?\\)/g, \"$1\")\n // Remove headings (## etc.)\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove bold/italic markers\n .replace(/\\*{1,3}(.*?)\\*{1,3}/g, \"$1\")\n .replace(/_{1,3}(.*?)_{1,3}/g, \"$1\")\n // Remove inline code\n .replace(/`([^`]*)`/g, \"$1\")\n // Remove blockquotes\n .replace(/^>\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^[-*_]{3,}\\s*$/gm, \"\")\n // Collapse whitespace\n .replace(/\\s+/g, \" \")\n .trim()\n );\n}\n\n/**\n * Extract the first ~maxLength characters from body text,\n * trimmed at a word boundary. Strips markdown syntax.\n */\nexport function generateExcerpt(body: string, maxLength = 160): string {\n const clean = stripMarkdown(body);\n\n if (clean.length <= maxLength) {\n return clean;\n }\n\n const truncated = clean.slice(0, maxLength);\n const lastSpace = truncated.lastIndexOf(\" \");\n\n if (lastSpace === -1) {\n return truncated;\n }\n\n return truncated.slice(0, lastSpace);\n}\n","import { generateExcerpt } from \"./excerpt.js\";\n\n/**\n * Generate a meta description. If an excerpt is provided, use it.\n * Otherwise, generate one from the body text.\n */\nexport function generateDescription(\n body: string,\n excerpt?: string,\n): string {\n return excerpt ?? generateExcerpt(body);\n}\n","import sharp from \"sharp\";\n\n/**\n * Read the width and height of an image file using sharp.\n * Returns undefined if the image cannot be read.\n */\nexport async function getImageDimensions(\n imagePath: string,\n): Promise<{ width: number; height: number } | undefined> {\n try {\n const metadata = await sharp(imagePath).metadata();\n if (metadata.width && metadata.height) {\n return { width: metadata.width, height: metadata.height };\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Count the number of words in a body of text.\n */\nexport function calcWordCount(body: string): number {\n return body.trim().split(/\\s+/).filter(Boolean).length;\n}\n","import { calcWordCount } from \"./word-count.js\";\n\n/**\n * Calculate estimated reading time in minutes from body text.\n * Assumes 200 words per minute.\n */\nexport function calcReadingTime(body: string): number {\n const words = calcWordCount(body);\n return Math.ceil(words / 200);\n}\n","/**\n * Generate a URL slug from a title string.\n * Lowercases, replaces spaces with hyphens, removes special characters,\n * and trims leading/trailing hyphens.\n */\nexport function generateSlug(title: string): string {\n return title\n .toLowerCase()\n .replace(/\\s+/g, \"-\")\n .replace(/[^a-z0-9-]/g, \"\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n","import type { TocItem } from \"../types.js\";\n\n/**\n * Generate a URL-friendly anchor slug from heading text.\n */\nfunction toAnchorSlug(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/**\n * Extract headings (## and ###) from markdown body and generate\n * a table of contents with anchor slugs.\n */\nexport function extractToc(body: string): TocItem[] {\n const headingRegex = /^(#{2,3})\\s+(.+)$/gm;\n const items: TocItem[] = [];\n let match: RegExpExecArray | null;\n\n while ((match = headingRegex.exec(body)) !== null) {\n const depth = match[1].length;\n const text = match[2].trim();\n items.push({\n depth,\n text,\n slug: toAnchorSlug(text),\n });\n }\n\n return items;\n}\n","import type { PartialVazaEntry, VazaEntry } from \"../types.js\";\nimport { generateBlurPlaceholder } from \"./blur-placeholder.js\";\nimport { generateDescription } from \"./description.js\";\nimport { generateExcerpt } from \"./excerpt.js\";\nimport { getImageDimensions } from \"./image-dimensions.js\";\nimport { calcReadingTime } from \"./reading-time.js\";\nimport { generateSlug } from \"./slug.js\";\nimport { extractToc } from \"./toc.js\";\nimport { calcWordCount } from \"./word-count.js\";\n\nexport {\n calcReadingTime,\n calcWordCount,\n extractToc,\n generateBlurPlaceholder,\n generateDescription,\n generateExcerpt,\n generateSlug,\n getImageDimensions,\n};\n\n/**\n * Convert a date value (string or Date) to a Date object.\n */\nfunction toDate(value: Date | string | undefined): Date | undefined {\n if (value === undefined) return undefined;\n if (value instanceof Date) return value;\n return new Date(value);\n}\n\n/**\n * Normalize a partial entry into a full VazaEntry.\n * For each field, keeps the existing value if present, otherwise generates it.\n */\nexport async function normalize(\n partial: PartialVazaEntry,\n): Promise<VazaEntry> {\n const { body, title } = partial;\n\n const slug = partial.slug || generateSlug(title);\n const wordCount = partial.wordCount ?? calcWordCount(body);\n const readingTime = partial.readingTime ?? calcReadingTime(body);\n const excerpt = partial.excerpt ?? generateExcerpt(body);\n const toc = partial.toc ?? extractToc(body);\n const description =\n partial.description ?? generateDescription(body, excerpt);\n const publishDate = toDate(partial.publishDate) ?? new Date();\n const updateDate = toDate(partial.updateDate);\n const eventDate = toDate(partial.eventDate);\n\n // Process image blur placeholder and dimensions if needed\n let image = partial.image\n ? { ...partial.image }\n : undefined;\n\n if (image?.src) {\n if (!image.blurDataURL) {\n image.blurDataURL = await generateBlurPlaceholder(image.src);\n }\n if (!image.width || !image.height) {\n const dims = await getImageDimensions(image.src);\n if (dims) {\n image.width = dims.width;\n image.height = dims.height;\n }\n }\n }\n\n const entry: VazaEntry = {\n slug,\n title,\n body,\n description,\n publishDate,\n readingTime,\n wordCount,\n excerpt,\n toc,\n ...(updateDate && { updateDate }),\n ...(image && { image: image as VazaEntry[\"image\"] }),\n ...(partial.tags && { tags: partial.tags }),\n ...(partial.category && { category: partial.category }),\n ...(partial.author && { author: partial.author as VazaEntry[\"author\"] }),\n ...(partial.relatedEntries && { relatedEntries: partial.relatedEntries }),\n ...(partial.canonical && { canonical: partial.canonical }),\n ...(partial.noindex !== undefined && { noindex: partial.noindex }),\n ...(partial.jsonLdType && { jsonLdType: partial.jsonLdType }),\n ...(partial.faqs && { faqs: partial.faqs }),\n ...(partial.steps && { steps: partial.steps }),\n ...(partial.price && { price: partial.price }),\n ...(partial.ingredients && { ingredients: partial.ingredients }),\n ...(partial.cookTime && { cookTime: partial.cookTime }),\n ...(eventDate && { eventDate }),\n ...(partial.eventLocation && { eventLocation: partial.eventLocation }),\n };\n\n return entry;\n}\n","import type { VazaEntry, VazaConfig, SitemapEntry } from \"../types.js\";\n\n/**\n * Generate sitemap entries from VazaEntry[].\n */\nexport function generateSitemap(\n entries: VazaEntry[],\n config: VazaConfig,\n): SitemapEntry[] {\n const siteUrl = config.site.url.replace(/\\/$/, \"\");\n const defaultChangeFreq = config.sitemap?.changeFrequency ?? \"weekly\";\n const defaultPriority = config.sitemap?.priority ?? 0.7;\n\n const sitemapEntries: SitemapEntry[] = [];\n\n for (const [, collection] of Object.entries(config.collections)) {\n const basePath = collection.basePath.replace(/\\/$/, \"\");\n\n for (const entry of entries) {\n const loc = `${siteUrl}${basePath}/${entry.slug}`;\n const lastmod = (entry.updateDate ?? entry.publishDate).toISOString();\n\n const sitemapEntry: SitemapEntry = {\n loc,\n lastmod,\n changefreq: defaultChangeFreq,\n priority: defaultPriority,\n };\n\n if (entry.image) {\n sitemapEntry.images = [\n {\n loc: entry.image.src.startsWith(\"http\")\n ? entry.image.src\n : `${siteUrl}${entry.image.src}`,\n title: entry.image.alt,\n },\n ];\n }\n\n sitemapEntries.push(sitemapEntry);\n }\n }\n\n return sitemapEntries;\n}\n\n/**\n * Render sitemap entries to valid XML with image namespace.\n */\nexport function renderSitemapXml(entries: SitemapEntry[]): string {\n const hasImages = entries.some((e) => e.images && e.images.length > 0);\n\n const lines: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"${hasImages ? ' xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"' : \"\"}>`,\n ];\n\n for (const entry of entries) {\n lines.push(\" <url>\");\n lines.push(` <loc>${escapeXml(entry.loc)}</loc>`);\n if (entry.lastmod) {\n lines.push(` <lastmod>${entry.lastmod}</lastmod>`);\n }\n if (entry.changefreq) {\n lines.push(` <changefreq>${entry.changefreq}</changefreq>`);\n }\n if (entry.priority !== undefined) {\n lines.push(` <priority>${entry.priority}</priority>`);\n }\n if (entry.images) {\n for (const img of entry.images) {\n lines.push(\" <image:image>\");\n lines.push(` <image:loc>${escapeXml(img.loc)}</image:loc>`);\n if (img.title) {\n lines.push(\n ` <image:title>${escapeXml(img.title)}</image:title>`,\n );\n }\n lines.push(\" </image:image>\");\n }\n }\n lines.push(\" </url>\");\n }\n\n lines.push(\"</urlset>\");\n return lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","import type { VazaEntry, VazaConfig, RssItem } from \"../types.js\";\n\n/**\n * Generate RSS items from entries, sorted by publishDate desc, limited by config.\n */\nexport function generateRss(\n entries: VazaEntry[],\n config: VazaConfig,\n): RssItem[] {\n const siteUrl = config.site.url.replace(/\\/$/, \"\");\n const limit = config.rss?.limit ?? 20;\n\n const sorted = [...entries].sort(\n (a, b) => b.publishDate.getTime() - a.publishDate.getTime(),\n );\n\n const limited = sorted.slice(0, limit);\n\n const items: RssItem[] = [];\n\n for (const [, collection] of Object.entries(config.collections)) {\n const basePath = collection.basePath.replace(/\\/$/, \"\");\n\n for (const entry of limited) {\n const link = `${siteUrl}${basePath}/${entry.slug}`;\n\n items.push({\n title: entry.title,\n link,\n description: entry.description || entry.excerpt,\n pubDate: entry.publishDate.toUTCString(),\n guid: link,\n author: entry.author?.name,\n });\n }\n }\n\n return items;\n}\n\n/**\n * Render RSS items to valid RSS 2.0 XML.\n */\nexport function renderRssXml(items: RssItem[], config: VazaConfig): string {\n const siteUrl = config.site.url.replace(/\\/$/, \"\");\n\n const lines: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n '<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">',\n \" <channel>\",\n ` <title>${escapeXml(config.site.name)}</title>`,\n ` <link>${escapeXml(siteUrl)}</link>`,\n ` <description>${escapeXml(config.site.description ?? \"\")}</description>`,\n ` <language>${config.site.language ?? \"en\"}</language>`,\n ` <lastBuildDate>${new Date().toUTCString()}</lastBuildDate>`,\n ];\n\n if (config.rss?.path) {\n lines.push(\n ` <atom:link href=\"${escapeXml(siteUrl + config.rss.path)}\" rel=\"self\" type=\"application/rss+xml\" />`,\n );\n }\n\n for (const item of items) {\n lines.push(\" <item>\");\n lines.push(` <title>${escapeXml(item.title)}</title>`);\n lines.push(` <link>${escapeXml(item.link)}</link>`);\n lines.push(\n ` <description>${escapeXml(item.description)}</description>`,\n );\n lines.push(` <pubDate>${item.pubDate}</pubDate>`);\n lines.push(` <guid isPermaLink=\"true\">${escapeXml(item.guid)}</guid>`);\n if (item.author) {\n lines.push(` <author>${escapeXml(item.author)}</author>`);\n }\n lines.push(\" </item>\");\n }\n\n lines.push(\" </channel>\");\n lines.push(\"</rss>\");\n return lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","import type { VazaEntry, SiteConfig } from \"../../types.js\";\n\n/**\n * Generate Article/BlogPosting schema.\n * Triggered when entry has title + publishDate + author.\n */\nexport function generateArticleSchema(\n entry: VazaEntry,\n site: SiteConfig,\n): Record<string, unknown> | null {\n if (!entry.title || !entry.publishDate || !entry.author) {\n return null;\n }\n\n const schema: Record<string, unknown> = {\n \"@context\": \"https://schema.org\",\n \"@type\": entry.jsonLdType === \"Article\" ? \"Article\" : \"BlogPosting\",\n headline: entry.title,\n description: entry.description || entry.excerpt,\n datePublished: entry.publishDate.toISOString(),\n author: {\n \"@type\": \"Person\",\n name: entry.author.name,\n ...(entry.author.url && { url: entry.author.url }),\n },\n publisher: {\n \"@type\": \"Organization\",\n name: site.name,\n ...(site.url && { url: site.url }),\n },\n };\n\n if (entry.updateDate) {\n schema.dateModified = entry.updateDate.toISOString();\n }\n\n if (entry.image) {\n schema.image = {\n \"@type\": \"ImageObject\",\n url: entry.image.src.startsWith(\"http\")\n ? entry.image.src\n : `${site.url.replace(/\\/$/, \"\")}${entry.image.src}`,\n ...(entry.image.width && { width: entry.image.width }),\n ...(entry.image.height && { height: entry.image.height }),\n };\n }\n\n if (entry.wordCount) {\n schema.wordCount = entry.wordCount;\n }\n\n return schema;\n}\n","import type { VazaEntry, SiteConfig } from \"../../types.js\";\n\n/**\n * Generate FAQPage schema.\n * Triggered when entry has faqs array with items.\n */\nexport function generateFaqSchema(\n entry: VazaEntry,\n _site: SiteConfig,\n): Record<string, unknown> | null {\n if (!entry.faqs || entry.faqs.length === 0) {\n return null;\n }\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"FAQPage\",\n mainEntity: entry.faqs.map((faq) => ({\n \"@type\": \"Question\",\n name: faq.question,\n acceptedAnswer: {\n \"@type\": \"Answer\",\n text: faq.answer,\n },\n })),\n };\n}\n","export interface BreadcrumbItem {\n name: string;\n url: string;\n}\n\n/**\n * Generate breadcrumb items from a slug.\n * Splits by \"/\" and creates array of {name, url} pairs.\n * Includes Home as first item.\n */\nexport function generateBreadcrumbs(\n slug: string,\n title: string,\n siteUrl: string,\n basePath: string,\n): BreadcrumbItem[] {\n const url = siteUrl.replace(/\\/$/, \"\");\n const path = basePath.replace(/\\/$/, \"\");\n\n const items: BreadcrumbItem[] = [{ name: \"Home\", url: `${url}/` }];\n\n // Add base path segments if they exist\n if (path) {\n const pathSegments = path.split(\"/\").filter(Boolean);\n let accumulated = url;\n for (const segment of pathSegments) {\n accumulated += `/${segment}`;\n items.push({\n name: segment.charAt(0).toUpperCase() + segment.slice(1),\n url: `${accumulated}/`,\n });\n }\n }\n\n // Split slug by \"/\" for nested slugs\n const slugSegments = slug.split(\"/\").filter(Boolean);\n\n if (slugSegments.length > 1) {\n let accumulated = `${url}${path}`;\n // Add intermediate slug segments (not the last one)\n for (let i = 0; i < slugSegments.length - 1; i++) {\n accumulated += `/${slugSegments[i]}`;\n const segmentName = slugSegments[i]\n .replace(/-/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n items.push({\n name: segmentName,\n url: `${accumulated}/`,\n });\n }\n }\n\n // Add the current page (final item uses the provided title)\n items.push({\n name: title,\n url: `${url}${path}/${slug}`,\n });\n\n return items;\n}\n","import type { VazaEntry, SiteConfig } from \"../../types.js\";\nimport { generateBreadcrumbs } from \"../breadcrumbs.js\";\n\n/**\n * Generate BreadcrumbList schema.\n * Always generated for every entry.\n */\nexport function generateBreadcrumbSchema(\n entry: VazaEntry,\n site: SiteConfig,\n basePath: string,\n): Record<string, unknown> {\n const siteUrl = site.url.replace(/\\/$/, \"\");\n const crumbs = generateBreadcrumbs(entry.slug, entry.title, siteUrl, basePath);\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"BreadcrumbList\",\n itemListElement: crumbs.map((crumb, index) => ({\n \"@type\": \"ListItem\",\n position: index + 1,\n name: crumb.name,\n item: crumb.url,\n })),\n };\n}\n","import type { VazaEntry, SiteConfig } from \"../../types.js\";\n\n/**\n * Generate HowTo schema.\n * Triggered when entry has steps array.\n */\nexport function generateHowToSchema(\n entry: VazaEntry,\n _site: SiteConfig,\n): Record<string, unknown> | null {\n if (!entry.steps || entry.steps.length === 0) {\n return null;\n }\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"HowTo\",\n name: entry.title,\n description: entry.description || entry.excerpt,\n step: entry.steps.map((step, index) => ({\n \"@type\": \"HowToStep\",\n position: index + 1,\n name: step.name,\n text: step.text,\n })),\n };\n}\n","import type { VazaEntry, SiteConfig } from \"../../types.js\";\n\n/**\n * Generate Product schema.\n * Triggered when entry has price.\n */\nexport function generateProductSchema(\n entry: VazaEntry,\n site: SiteConfig,\n): Record<string, unknown> | null {\n if (!entry.price) {\n return null;\n }\n\n const schema: Record<string, unknown> = {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Product\",\n name: entry.title,\n description: entry.description || entry.excerpt,\n offers: {\n \"@type\": \"Offer\",\n price: entry.price.amount,\n priceCurrency: entry.price.currency,\n availability: \"https://schema.org/InStock\",\n },\n };\n\n if (entry.image) {\n schema.image = entry.image.src.startsWith(\"http\")\n ? entry.image.src\n : `${site.url.replace(/\\/$/, \"\")}${entry.image.src}`;\n }\n\n return schema;\n}\n","import type { VazaEntry, SiteConfig } from \"../../types.js\";\n\n/**\n * Generate Recipe schema.\n * Triggered when entry has ingredients.\n */\nexport function generateRecipeSchema(\n entry: VazaEntry,\n _site: SiteConfig,\n): Record<string, unknown> | null {\n if (!entry.ingredients || entry.ingredients.length === 0) {\n return null;\n }\n\n const schema: Record<string, unknown> = {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Recipe\",\n name: entry.title,\n description: entry.description || entry.excerpt,\n recipeIngredient: entry.ingredients,\n };\n\n if (entry.cookTime) {\n schema.cookTime = entry.cookTime;\n }\n\n if (entry.author) {\n schema.author = {\n \"@type\": \"Person\",\n name: entry.author.name,\n };\n }\n\n if (entry.image) {\n schema.image = entry.image.src;\n }\n\n return schema;\n}\n","import type { VazaEntry, SiteConfig } from \"../../types.js\";\n\n/**\n * Generate Event schema.\n * Triggered when entry has eventDate.\n */\nexport function generateEventSchema(\n entry: VazaEntry,\n site: SiteConfig,\n): Record<string, unknown> | null {\n if (!entry.eventDate) {\n return null;\n }\n\n const schema: Record<string, unknown> = {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Event\",\n name: entry.title,\n description: entry.description || entry.excerpt,\n startDate: entry.eventDate.toISOString(),\n organizer: {\n \"@type\": \"Organization\",\n name: site.name,\n url: site.url,\n },\n };\n\n if (entry.eventLocation) {\n schema.location = {\n \"@type\": \"Place\",\n name: entry.eventLocation,\n };\n }\n\n if (entry.image) {\n schema.image = entry.image.src.startsWith(\"http\")\n ? entry.image.src\n : `${site.url.replace(/\\/$/, \"\")}${entry.image.src}`;\n }\n\n return schema;\n}\n","import type { VazaEntry, VazaConfig, SiteConfig } from \"../../types.js\";\nimport { generateArticleSchema } from \"./article.js\";\nimport { generateFaqSchema } from \"./faq.js\";\nimport { generateBreadcrumbSchema } from \"./breadcrumb.js\";\nimport { generateHowToSchema } from \"./how-to.js\";\nimport { generateProductSchema } from \"./product.js\";\nimport { generateRecipeSchema } from \"./recipe.js\";\nimport { generateEventSchema } from \"./event.js\";\n\ntype SchemaGenerator = (\n entry: VazaEntry,\n site: SiteConfig,\n basePath: string,\n) => Record<string, unknown> | null;\n\n/**\n * Built-in schema generators. Each is tried for every entry;\n * if it returns non-null the schema is included.\n */\nconst builtinGenerators: SchemaGenerator[] = [\n (entry, site) => generateArticleSchema(entry, site),\n (entry, site) => generateFaqSchema(entry, site),\n (entry, site, basePath) => generateBreadcrumbSchema(entry, site, basePath),\n (entry, site) => generateHowToSchema(entry, site),\n (entry, site) => generateProductSchema(entry, site),\n (entry, site) => generateRecipeSchema(entry, site),\n (entry, site) => generateEventSchema(entry, site),\n];\n\n/**\n * Auto-detect which schemas apply to each entry and generate them.\n * Returns slug -> array of schema objects.\n */\nexport function generateJsonLd(\n entries: VazaEntry[],\n config: VazaConfig,\n): Record<string, Record<string, unknown>[]> {\n const result: Record<string, Record<string, unknown>[]> = {};\n const site = config.site;\n\n // Determine basePath from the first collection (or empty string)\n const collectionKeys = Object.keys(config.collections);\n const basePath =\n collectionKeys.length > 0\n ? config.collections[collectionKeys[0]].basePath\n : \"\";\n\n // Collect custom schema generators from config\n const customGenerators: SchemaGenerator[] = [];\n if (config.jsonLd?.customSchemas) {\n for (const [, generator] of Object.entries(config.jsonLd.customSchemas)) {\n customGenerators.push((entry, s) => generator(entry, s));\n }\n }\n\n const allGenerators = [...builtinGenerators, ...customGenerators];\n\n for (const entry of entries) {\n const schemas: Record<string, unknown>[] = [];\n\n for (const generator of allGenerators) {\n const schema = generator(entry, site, basePath);\n if (schema) {\n schemas.push(schema);\n }\n }\n\n if (schemas.length > 0) {\n result[entry.slug] = schemas;\n }\n }\n\n return result;\n}\n","import { writeFileSync, existsSync, mkdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { VazaEntry, VazaConfig, OgImagesConfig } from \"../types.js\";\n\n/**\n * Generate OG images using satori + @resvg/resvg-js.\n * Returns slug -> output path map.\n * Skips if file already exists and entry hasn't changed.\n */\nexport async function generateOgImages(\n entries: VazaEntry[],\n config: VazaConfig,\n): Promise<Record<string, string>> {\n const ogConfig = config.ogImages;\n if (!ogConfig?.enabled) return {};\n\n const outputDir = ogConfig.outputDir ?? join(process.cwd(), \"public\", \"og\");\n const template = ogConfig.template ?? \"minimal\";\n\n // Ensure output directory exists\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n\n // Dynamic imports for optional dependencies\n const satori = await import(\"satori\").then((m) => m.default ?? m);\n const { Resvg } = await import(\"@resvg/resvg-js\");\n\n // Load template renderer\n const templateRenderer = ogConfig.render ?? (await loadTemplate(template));\n\n const results: Record<string, string> = {};\n\n for (const entry of entries) {\n const outputPath = join(outputDir, `${entry.slug}.png`);\n\n // Skip if file already exists and is newer than the entry's update\n if (existsSync(outputPath)) {\n const stat = statSync(outputPath);\n const entryDate = entry.updateDate ?? entry.publishDate;\n if (stat.mtime >= entryDate) {\n results[entry.slug] = outputPath;\n continue;\n }\n }\n\n // Render the template\n const element = templateRenderer(entry, ogConfig);\n\n // Convert to SVG with satori\n const svg = await satori(element as Parameters<typeof satori>[0], {\n width: 1200,\n height: 630,\n fonts: [\n {\n name: \"sans-serif\",\n data: new ArrayBuffer(0),\n weight: 400,\n style: \"normal\" as const,\n },\n ],\n });\n\n // Convert SVG to PNG with resvg\n const resvg = new Resvg(svg, {\n fitTo: { mode: \"width\" as const, value: 1200 },\n });\n const pngData = resvg.render();\n const pngBuffer = pngData.asPng();\n\n // Ensure parent directory exists for nested slugs\n const parentDir = join(outputPath, \"..\");\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true });\n }\n\n writeFileSync(outputPath, pngBuffer);\n results[entry.slug] = outputPath;\n }\n\n return results;\n}\n\n/**\n * Load a built-in template renderer.\n */\nasync function loadTemplate(\n template: string,\n): Promise<(entry: VazaEntry, config: OgImagesConfig) => unknown> {\n switch (template) {\n case \"blog\": {\n const mod = await import(\"./og-templates/blog.js\");\n return mod.blogTemplate;\n }\n case \"product\": {\n const mod = await import(\"./og-templates/product.js\");\n return mod.productTemplate;\n }\n case \"dark\": {\n const mod = await import(\"./og-templates/dark.js\");\n return mod.darkTemplate;\n }\n case \"minimal\":\n default: {\n const mod = await import(\"./og-templates/minimal.js\");\n return mod.minimalTemplate;\n }\n }\n}\n","import type { VazaEntry, VazaConfig, TaxonomyData } from \"../types.js\";\n\n/**\n * Build tag and category maps from entries.\n */\nexport function generateTaxonomy(\n entries: VazaEntry[],\n _config: VazaConfig,\n): TaxonomyData {\n const tags: Record<string, string[]> = {};\n const categories: Record<string, string[]> = {};\n\n for (const entry of entries) {\n if (entry.tags) {\n for (const tag of entry.tags) {\n const normalized = tag.toLowerCase().trim();\n if (!tags[normalized]) {\n tags[normalized] = [];\n }\n tags[normalized].push(entry.slug);\n }\n }\n\n if (entry.category) {\n const normalized = entry.category.toLowerCase().trim();\n if (!categories[normalized]) {\n categories[normalized] = [];\n }\n categories[normalized].push(entry.slug);\n }\n }\n\n return { tags, categories };\n}\n","import { execSync } from \"node:child_process\";\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport type { VazaEntry, VazaConfig, RedirectEntry } from \"../types.js\";\n\nconst MANIFEST_DIR = \".vaza-content\";\nconst MANIFEST_FILE = \"slugs.json\";\n\n/**\n * Detect slug changes that need redirects.\n * Tries git diff first, falls back to reading/writing a slugs.json manifest.\n */\nexport async function detectRedirects(\n entries: VazaEntry[],\n config: VazaConfig,\n): Promise<RedirectEntry[]> {\n const strategy = config.redirects?.strategy ?? \"both\";\n const redirects: RedirectEntry[] = [];\n\n if (strategy === \"git\" || strategy === \"both\") {\n const gitRedirects = detectGitRenames();\n redirects.push(...gitRedirects);\n }\n\n if (strategy === \"manifest\" || strategy === \"both\") {\n const manifestRedirects = detectManifestChanges(entries);\n redirects.push(...manifestRedirects);\n }\n\n // Deduplicate by \"from\" path\n const seen = new Set<string>();\n const unique: RedirectEntry[] = [];\n for (const r of redirects) {\n if (!seen.has(r.from)) {\n seen.add(r.from);\n unique.push(r);\n }\n }\n\n return unique;\n}\n\n/**\n * Detect renames via git log.\n */\nfunction detectGitRenames(): RedirectEntry[] {\n const redirects: RedirectEntry[] = [];\n\n try {\n const output = execSync(\n \"git log --all --diff-filter=R --summary --format=\",\n {\n encoding: \"utf-8\",\n timeout: 10_000,\n },\n );\n\n const renameRegex =\n /rename\\s+(.+?)\\{(.+?)\\s*=>\\s*(.+?)\\}\\s*\\((\\d+)%\\)/g;\n const simpleRenameRegex = /rename\\s+(.+?)\\s+=>\\s+(.+?)\\s+\\((\\d+)%\\)/g;\n\n let match: RegExpExecArray | null;\n\n match = renameRegex.exec(output);\n while (match) {\n const prefix = match[1];\n const oldPart = match[2].trim();\n const newPart = match[3].trim();\n\n const oldPath = `${prefix}${oldPart}`;\n const newPath = `${prefix}${newPart}`;\n\n const oldSlug = extractSlug(oldPath);\n const newSlug = extractSlug(newPath);\n\n if (oldSlug && newSlug && oldSlug !== newSlug) {\n redirects.push({\n from: oldSlug,\n to: newSlug,\n status: 301,\n });\n }\n match = renameRegex.exec(output);\n }\n\n match = simpleRenameRegex.exec(output);\n while (match) {\n const oldPath = match[1].trim();\n const newPath = match[2].trim();\n\n const oldSlug = extractSlug(oldPath);\n const newSlug = extractSlug(newPath);\n\n if (oldSlug && newSlug && oldSlug !== newSlug) {\n redirects.push({\n from: oldSlug,\n to: newSlug,\n status: 301,\n });\n }\n match = simpleRenameRegex.exec(output);\n }\n } catch {\n // Git not available or not a git repo, silently skip\n }\n\n return redirects;\n}\n\n/**\n * Detect changes by comparing current slugs against a stored manifest.\n */\nfunction detectManifestChanges(entries: VazaEntry[]): RedirectEntry[] {\n const redirects: RedirectEntry[] = [];\n const manifestPath = join(process.cwd(), MANIFEST_DIR, MANIFEST_FILE);\n\n const currentSlugs = entries.map((e) => e.slug).sort();\n\n if (existsSync(manifestPath)) {\n try {\n const raw = readFileSync(manifestPath, \"utf-8\");\n const previousSlugs: string[] = JSON.parse(raw);\n\n // Find slugs that existed before but no longer exist\n const currentSet = new Set(currentSlugs);\n const removedSlugs = previousSlugs.filter((s) => !currentSet.has(s));\n\n // For removed slugs, try to find a close match in current slugs\n // (simple heuristic: if only one slug was removed and one was added, assume rename)\n const previousSet = new Set(previousSlugs);\n const addedSlugs = currentSlugs.filter((s) => !previousSet.has(s));\n\n if (removedSlugs.length === 1 && addedSlugs.length === 1) {\n redirects.push({\n from: removedSlugs[0],\n to: addedSlugs[0],\n status: 301,\n });\n }\n } catch {\n // Corrupted manifest, will be overwritten\n }\n }\n\n // Write current slugs to manifest\n const dir = dirname(manifestPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(manifestPath, JSON.stringify(currentSlugs, null, 2));\n\n return redirects;\n}\n\n/**\n * Extract a slug from a file path by removing directory prefix and extension.\n */\nfunction extractSlug(filePath: string): string | null {\n const name = filePath.split(\"/\").pop();\n if (!name) return null;\n return name.replace(/\\.(mdx?|tsx?|jsx?)$/, \"\");\n}\n","import type { IntegrityIssue, Severity, VazaEntry } from \"../types.js\";\n\n/**\n * Check if entries with images have alt text.\n * Also scan body for markdown images  with empty alt text.\n */\nexport function checkAltText(\n entries: VazaEntry[],\n severity: Severity = \"warn\",\n): IntegrityIssue[] {\n const issues: IntegrityIssue[] = [];\n\n // Match markdown images with empty alt: \n const emptyAltRegex = /!\\[\\s*\\]\\([^)]+\\)/g;\n\n for (const entry of entries) {\n // Check featured image alt text\n if (entry.image && !entry.image.alt?.trim()) {\n issues.push({\n type: \"missing-alt-text\",\n severity,\n message: `Featured image is missing alt text (src: \"${entry.image.src}\")`,\n slug: entry.slug,\n });\n }\n\n // Check body for images with empty alt text\n emptyAltRegex.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = emptyAltRegex.exec(entry.body)) !== null) {\n issues.push({\n type: \"missing-alt-text\",\n severity,\n message: `Markdown image with empty alt text: ${match[0]}`,\n slug: entry.slug,\n });\n }\n }\n\n return issues;\n}\n","import type { IntegrityIssue, Severity, VazaEntry } from \"../types.js\";\n\n/**\n * Scan each entry's body for internal markdown links [text](/path)\n * and check if any entry has a matching slug/path.\n */\nexport function checkBrokenLinks(\n entries: VazaEntry[],\n severity: Severity = \"warn\",\n): IntegrityIssue[] {\n const slugs = new Set(entries.map((e) => e.slug));\n const issues: IntegrityIssue[] = [];\n\n // Match markdown links with internal paths: [text](/some-path)\n const linkRegex = /\\[([^\\]]*)\\]\\(\\/([^)]*)\\)/g;\n\n for (const entry of entries) {\n let match: RegExpExecArray | null;\n linkRegex.lastIndex = 0;\n\n while ((match = linkRegex.exec(entry.body)) !== null) {\n const rawPath = match[2];\n // Strip trailing slash, query string, and hash\n const cleanPath = rawPath.split(/[?#]/)[0].replace(/\\/$/, \"\");\n // Normalize: remove leading slashes, take last segment as potential slug\n const segments = cleanPath.split(\"/\").filter(Boolean);\n const lastSegment = segments[segments.length - 1];\n\n // Check if any slug matches the full clean path or the last segment\n const found =\n slugs.has(cleanPath) ||\n (lastSegment !== undefined && slugs.has(lastSegment));\n\n if (!found) {\n issues.push({\n type: \"broken-link\",\n severity,\n message: `Broken internal link to \"/${rawPath}\"`,\n slug: entry.slug,\n });\n }\n }\n }\n\n return issues;\n}\n","import type { IntegrityIssue, Severity, VazaEntry } from \"../types.js\";\n\n/**\n * Normalize a title for comparison: lowercase, collapse whitespace,\n * strip punctuation.\n */\nfunction normalize(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^\\w\\s]/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * Check for entries with identical or very similar titles.\n * Uses case-insensitive normalized comparison.\n */\nexport function checkDuplicateContent(\n entries: VazaEntry[],\n severity: Severity = \"warn\",\n): IntegrityIssue[] {\n const issues: IntegrityIssue[] = [];\n const seen = new Map<string, string[]>();\n\n for (const entry of entries) {\n const key = normalize(entry.title);\n if (!key) continue;\n\n const existing = seen.get(key);\n if (existing) {\n existing.push(entry.slug);\n } else {\n seen.set(key, [entry.slug]);\n }\n }\n\n for (const [, slugs] of seen) {\n if (slugs.length > 1) {\n issues.push({\n type: \"duplicate-content\",\n severity,\n message: `Entries have near-identical titles: ${slugs.join(\", \")}`,\n slug: slugs[0],\n });\n }\n }\n\n return issues;\n}\n","import type { IntegrityIssue, Severity, VazaEntry } from \"../types.js\";\n\n/**\n * Check for duplicate slugs across all entries.\n */\nexport function checkDuplicateSlugs(\n entries: VazaEntry[],\n severity: Severity = \"error\",\n): IntegrityIssue[] {\n const issues: IntegrityIssue[] = [];\n const seen = new Map<string, number>();\n\n for (const entry of entries) {\n const count = seen.get(entry.slug) ?? 0;\n seen.set(entry.slug, count + 1);\n }\n\n for (const [slug, count] of seen) {\n if (count > 1) {\n issues.push({\n type: \"duplicate-slug\",\n severity,\n message: `Slug \"${slug}\" is used by ${count} entries`,\n slug,\n });\n }\n }\n\n return issues;\n}\n","import type {\n IntegrityConfig,\n IntegrityIssue,\n Severity,\n VazaEntry,\n} from \"../types.js\";\n\n/**\n * Parse a maxAge string like \"6m\", \"1y\", \"90d\" into milliseconds.\n */\nfunction parseMaxAge(maxAge: string): number {\n const match = maxAge.match(/^(\\d+)(d|m|y)$/);\n if (!match) {\n throw new Error(\n `Invalid maxAge format: \"${maxAge}\". Use \"90d\", \"6m\", or \"1y\".`,\n );\n }\n\n const value = Number.parseInt(match[1], 10);\n const unit = match[2];\n\n const MS_PER_DAY = 86_400_000;\n\n switch (unit) {\n case \"d\":\n return value * MS_PER_DAY;\n case \"m\":\n return value * 30 * MS_PER_DAY;\n case \"y\":\n return value * 365 * MS_PER_DAY;\n default:\n return value * MS_PER_DAY;\n }\n}\n\n/**\n * Check if any entry's updateDate (or publishDate) is older than maxAge.\n */\nexport function checkFreshness(\n entries: VazaEntry[],\n config: IntegrityConfig,\n): IntegrityIssue[] {\n const issues: IntegrityIssue[] = [];\n\n const maxAgeStr = config.freshness?.maxAge ?? \"6m\";\n const severity: Severity = config.freshness?.severity ?? \"warn\";\n\n if (severity === \"off\") return issues;\n\n const maxAgeMs = parseMaxAge(maxAgeStr);\n const now = Date.now();\n\n for (const entry of entries) {\n const lastDate = entry.updateDate ?? entry.publishDate;\n const age = now - lastDate.getTime();\n\n if (age > maxAgeMs) {\n const daysOld = Math.floor(age / 86_400_000);\n issues.push({\n type: \"stale-content\",\n severity,\n message: `Content is ${daysOld} days old (max age: ${maxAgeStr})`,\n slug: entry.slug,\n });\n }\n }\n\n return issues;\n}\n","import type {\n IntegrityConfig,\n IntegrityIssue,\n VazaEntry,\n} from \"../types.js\";\n\n/**\n * Check title length and description length against configured maximums.\n */\nexport function checkMetaLength(\n entries: VazaEntry[],\n config: IntegrityConfig,\n): IntegrityIssue[] {\n const issues: IntegrityIssue[] = [];\n\n const titleMax = config.metaTitleLength?.max ?? 60;\n const titleSeverity = config.metaTitleLength?.severity ?? \"warn\";\n\n const descMax = config.metaDescriptionLength?.max ?? 120;\n const descSeverity = config.metaDescriptionLength?.severity ?? \"warn\";\n\n for (const entry of entries) {\n if (titleSeverity !== \"off\" && entry.title.length > titleMax) {\n issues.push({\n type: \"meta-title-length\",\n severity: titleSeverity,\n message: `Title is ${entry.title.length} chars (max ${titleMax}): \"${entry.title}\"`,\n slug: entry.slug,\n });\n }\n\n if (descSeverity !== \"off\" && entry.description.length > descMax) {\n issues.push({\n type: \"meta-description-length\",\n severity: descSeverity,\n message: `Description is ${entry.description.length} chars (max ${descMax})`,\n slug: entry.slug,\n });\n }\n }\n\n return issues;\n}\n","import type { IntegrityIssue, Severity, VazaEntry } from \"../types.js\";\n\n/**\n * Check if any entry has zero inbound internal links from other entries.\n */\nexport function checkOrphanPages(\n entries: VazaEntry[],\n severity: Severity = \"warn\",\n): IntegrityIssue[] {\n const issues: IntegrityIssue[] = [];\n\n // Build a set of slugs that are linked to by at least one other entry\n const linkedSlugs = new Set<string>();\n const linkRegex = /\\[([^\\]]*)\\]\\(\\/([^)]*)\\)/g;\n\n for (const entry of entries) {\n linkRegex.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = linkRegex.exec(entry.body)) !== null) {\n const rawPath = match[2];\n const cleanPath = rawPath.split(/[?#]/)[0].replace(/\\/$/, \"\");\n const segments = cleanPath.split(\"/\").filter(Boolean);\n const lastSegment = segments[segments.length - 1];\n\n // Add both the full path and last segment as potential targets\n linkedSlugs.add(cleanPath);\n if (lastSegment !== undefined) {\n linkedSlugs.add(lastSegment);\n }\n }\n }\n\n // Check which entries have no inbound links\n for (const entry of entries) {\n if (!linkedSlugs.has(entry.slug)) {\n issues.push({\n type: \"orphan-page\",\n severity,\n message: `No other entry links to \"${entry.slug}\"`,\n slug: entry.slug,\n });\n }\n }\n\n return issues;\n}\n","import type {\n IntegrityConfig,\n IntegrityIssue,\n IntegrityReport,\n VazaConfig,\n VazaEntry,\n} from \"../types.js\";\nimport { checkAltText } from \"./alt-text.js\";\nimport { checkBrokenLinks } from \"./broken-links.js\";\nimport { checkDuplicateContent } from \"./duplicate-content.js\";\nimport { checkDuplicateSlugs } from \"./duplicate-slugs.js\";\nimport { checkFreshness } from \"./freshness.js\";\nimport { checkMetaLength } from \"./meta-length.js\";\nimport { checkOrphanPages } from \"./orphan-pages.js\";\n\n/**\n * Run all integrity checks based on config severity settings.\n * Checks with severity \"off\" are skipped entirely.\n */\nexport function checkIntegrity(\n entries: VazaEntry[],\n config: VazaConfig,\n): IntegrityReport {\n const ic: IntegrityConfig = config.integrity ?? {};\n const issues: IntegrityIssue[] = [];\n\n // Broken links\n if (ic.brokenLinks !== \"off\") {\n issues.push(...checkBrokenLinks(entries, ic.brokenLinks ?? \"warn\"));\n }\n\n // Meta length (has its own internal severity handling)\n const titleOff = ic.metaTitleLength?.severity === \"off\";\n const descOff = ic.metaDescriptionLength?.severity === \"off\";\n if (!titleOff || !descOff) {\n issues.push(...checkMetaLength(entries, ic));\n }\n\n // Alt text\n if (ic.altText !== \"off\") {\n issues.push(...checkAltText(entries, ic.altText ?? \"warn\"));\n }\n\n // Duplicate slugs\n if (ic.duplicateSlugs !== \"off\") {\n issues.push(...checkDuplicateSlugs(entries, ic.duplicateSlugs ?? \"error\"));\n }\n\n // Duplicate content\n if (ic.duplicateContent !== \"off\") {\n issues.push(\n ...checkDuplicateContent(entries, ic.duplicateContent ?? \"warn\"),\n );\n }\n\n // Orphan pages\n if (ic.orphanPages !== \"off\") {\n issues.push(...checkOrphanPages(entries, ic.orphanPages ?? \"warn\"));\n }\n\n // Freshness\n if (ic.freshness?.severity !== \"off\") {\n issues.push(...checkFreshness(entries, ic));\n }\n\n // Compute summary\n let errors = 0;\n let warnings = 0;\n for (const issue of issues) {\n if (issue.severity === \"error\") errors++;\n else if (issue.severity === \"warn\") warnings++;\n }\n\n return { issues, summary: { errors, warnings } };\n}\n\nexport { checkAltText } from \"./alt-text.js\";\nexport { checkBrokenLinks } from \"./broken-links.js\";\nexport { checkDuplicateContent } from \"./duplicate-content.js\";\nexport { checkDuplicateSlugs } from \"./duplicate-slugs.js\";\nexport { checkFreshness } from \"./freshness.js\";\nexport { checkMetaLength } from \"./meta-length.js\";\nexport { checkOrphanPages } from \"./orphan-pages.js\";\n","import type {\n VazaConfig,\n VazaEntry,\n VazaOutput,\n PartialVazaEntry,\n} from \"./types.js\";\nimport { normalize } from \"./normalize/index.js\";\nimport { generateSitemap } from \"./generate/sitemap.js\";\nimport { generateRss } from \"./generate/rss.js\";\nimport { generateJsonLd } from \"./generate/json-ld/index.js\";\nimport { generateOgImages } from \"./generate/og-images.js\";\nimport { generateTaxonomy } from \"./generate/taxonomy.js\";\nimport { detectRedirects } from \"./generate/redirects.js\";\nimport { checkIntegrity } from \"./integrity/index.js\";\n\nexport async function processCollections(\n config: VazaConfig,\n): Promise<VazaOutput> {\n const allEntries: VazaEntry[] = [];\n\n // Collect and normalize entries from all collections\n for (const [name, collection] of Object.entries(config.collections)) {\n const rawEntries = await collection.entries();\n const partialEntries: PartialVazaEntry[] = rawEntries.map(collection.map);\n\n for (const partial of partialEntries) {\n const normalized = await normalize(partial);\n allEntries.push(normalized);\n }\n }\n\n // Generate all SEO outputs in parallel\n const [sitemap, rss, jsonLd, ogImagePaths, taxonomy, redirects, integrity] =\n await Promise.all([\n config.sitemap?.enabled !== false\n ? generateSitemap(allEntries, config)\n : Promise.resolve([]),\n config.rss?.enabled !== false\n ? generateRss(allEntries, config)\n : Promise.resolve([]),\n config.jsonLd?.enabled !== false\n ? generateJsonLd(allEntries, config)\n : Promise.resolve({}),\n config.ogImages?.enabled !== false\n ? generateOgImages(allEntries, config)\n : Promise.resolve({}),\n config.taxonomy?.enabled !== false\n ? generateTaxonomy(allEntries, config)\n : Promise.resolve({ tags: {}, categories: {} }),\n config.redirects?.enabled !== false\n ? detectRedirects(allEntries, config)\n : Promise.resolve([]),\n checkIntegrity(allEntries, config),\n ]);\n\n // Print integrity report to terminal\n if (integrity.summary.errors > 0 || integrity.summary.warnings > 0) {\n printIntegrityReport(integrity);\n }\n\n return {\n entries: allEntries,\n sitemap,\n rss,\n jsonLd,\n ogImagePaths,\n taxonomy,\n redirects,\n integrity,\n };\n}\n\nfunction printIntegrityReport(\n report: VazaOutput[\"integrity\"],\n): void {\n const { issues, summary } = report;\n\n console.log(\"\\n[vaza-content] Integrity Report\");\n console.log(\"─\".repeat(50));\n\n for (const issue of issues) {\n const icon = issue.severity === \"error\" ? \"✖\" : \"⚠\";\n const prefix = issue.severity === \"error\" ? \"ERROR\" : \"WARN\";\n const location = issue.slug ? ` (${issue.slug})` : \"\";\n console.log(` ${icon} [${prefix}] ${issue.message}${location}`);\n }\n\n console.log(\"─\".repeat(50));\n console.log(\n ` ${summary.errors} error(s), ${summary.warnings} warning(s)\\n`,\n );\n\n if (summary.errors > 0) {\n throw new Error(\n `[vaza-content] Build failed: ${summary.errors} integrity error(s) found.`,\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-DGUM43GV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports.__require = __require;
|
|
11
|
+
//# sourceMappingURL=chunk-JEQ2X3Z6.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/mehdi/vaza-content/dist/chunk-JEQ2X3Z6.cjs"],"names":[],"mappings":"AAAA,6EAAI,UAAU,kBAAkB,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,QAAQ,IAAI,YAAY,EAAE,QAAQ,EAAE,OAAO,MAAM,IAAI,YAAY,EAAE,IAAI,KAAK,CAAC,CAAC,EAAE;AAC/H,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,QAAQ,IAAI,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE;AACpB,EAAE,GAAG,CAAC,OAAO,QAAQ,IAAI,WAAW,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;AAC3E,EAAE,MAAM,KAAK,CAAC,uBAAuB,EAAE,EAAE,EAAE,oBAAoB,CAAC;AAChE,CAAC,CAAC;AACF;AACA;AACE;AACF,8BAAC","file":"/Users/mehdi/vaza-content/dist/chunk-JEQ2X3Z6.cjs"}
|