blume 1.1.0 → 1.1.2

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cli/index.js +319 -91
  3. package/dist/cli/index.js.map +18 -18
  4. package/dist/types/ai/component-markdown.d.ts +12 -1
  5. package/dist/types/core/config-input.d.ts +18 -8
  6. package/dist/types/core/schema.d.ts +38 -24
  7. package/dist/types/core/types.d.ts +14 -0
  8. package/dist/types/markdown/themes.d.ts +21 -0
  9. package/docs/advanced/changelog.mdx +1 -1
  10. package/docs/configuration/ai.mdx +2 -2
  11. package/docs/configuration/index.mdx +1 -1
  12. package/docs/configuration/seo.mdx +16 -0
  13. package/docs/content/components.mdx +1 -1
  14. package/docs/content/navigation.mdx +4 -0
  15. package/docs/content/syntax.mdx +14 -0
  16. package/docs/reference/cli.mdx +1 -1
  17. package/docs/reference/frontmatter.mdx +2 -0
  18. package/package.json +2 -1
  19. package/src/ai/component-markdown.ts +39 -11
  20. package/src/ai/llms.ts +4 -2
  21. package/src/ai/markdown.ts +5 -1
  22. package/src/astro/generate.ts +121 -32
  23. package/src/astro/pages.ts +21 -5
  24. package/src/astro/templates.ts +70 -7
  25. package/src/audit/checks/llms.ts +4 -1
  26. package/src/cli/commands/build.ts +13 -1
  27. package/src/cli/prepare.ts +10 -2
  28. package/src/components/content/Component.astro +99 -6
  29. package/src/components/content/diff.ts +53 -4
  30. package/src/components/layout/RootLayout.astro +9 -1
  31. package/src/components/layout/nav-utils.ts +18 -7
  32. package/src/core/config-input.ts +18 -8
  33. package/src/core/diagnostics.ts +2 -0
  34. package/src/core/graph.ts +23 -4
  35. package/src/core/navigation.ts +180 -21
  36. package/src/core/project-graph.ts +54 -28
  37. package/src/core/schema.ts +39 -2
  38. package/src/core/sources/github-releases.ts +65 -2
  39. package/src/core/types.ts +14 -0
  40. package/src/markdown/index.ts +3 -0
  41. package/src/markdown/inline-code.ts +1 -1
  42. package/src/markdown/themes.ts +7 -2
  43. package/src/markdown/twoslash.ts +60 -0
  44. package/src/registry/eject.ts +3 -1
  45. /package/docs/{03-faq.mdx → 07-faq.mdx} +0 -0
package/dist/cli/index.js CHANGED
@@ -308,12 +308,14 @@ var DOCS_PATHS = {
308
308
  BLUME_CONTENT_ROOT_MISSING: DOCS_CONTENT_SOURCES,
309
309
  BLUME_DEAD_LINK: DOCS_REFERENCE_CLI,
310
310
  BLUME_DUPLICATE_ROUTE: DOCS_CONTENT_NAVIGATION,
311
+ BLUME_DUPLICATE_SIDEBAR_ORDER: DOCS_CONTENT_NAVIGATION,
311
312
  BLUME_FRONTMATTER_INVALID: "/docs/reference/frontmatter",
312
313
  BLUME_META_INVALID: "/docs/content/meta",
313
314
  BLUME_META_LOAD_FAILED: "/docs/content/meta",
314
315
  BLUME_MISSING_SECRET: DOCS_DEPLOYMENT,
315
316
  BLUME_NAV_DUPLICATE_LABEL: DOCS_CONTENT_NAVIGATION,
316
317
  BLUME_NAV_HIDDEN_IN_SIDEBAR: DOCS_CONTENT_NAVIGATION,
318
+ BLUME_NAV_INDEX_TITLE_MISMATCH: DOCS_CONTENT_NAVIGATION,
317
319
  BLUME_NAV_MISSING_PAGE: DOCS_CONTENT_NAVIGATION,
318
320
  BLUME_NODE_VERSION: "/docs/quickstart",
319
321
  BLUME_SERVER_FEATURE_REQUIRED: DOCS_DEPLOYMENT,
@@ -2301,7 +2303,7 @@ var llmsChecks = {
2301
2303
  continue;
2302
2304
  }
2303
2305
  listed.add(path);
2304
- if (!context.byUrl.has(path)) {
2306
+ if (!context.byUrl.has(path) && !context.files.has(path)) {
2305
2307
  found.push(finding("BLUME_AUDIT_LLMS_TXT_STALE_ENTRY", { file: llms.file, line: entry.line, url: entry.url }, `llms.txt lists ${path}, which the build does not serve.`));
2306
2308
  }
2307
2309
  }
@@ -3531,9 +3533,7 @@ var astroConfigTemplate = (options) => {
3531
3533
  ];
3532
3534
  const blumeImport = `import { ${blumeImports.join(", ")} } from "blume/astro";
3533
3535
  `;
3534
- const twoslashImport = `import { transformerTwoslash } from "@shikijs/twoslash";
3535
- `;
3536
- const twoslashTransformer = "transformerTwoslash({ explicitTrigger: true }), ";
3536
+ const twoslashTransformer = "blumeTwoslashTransformer(), ";
3537
3537
  const deployBase = normalizeBasePath(deployment.base);
3538
3538
  const integrations = [
3539
3539
  `mdx({ processor: blumeMdxProcessor(${JSON.stringify({
@@ -3557,8 +3557,8 @@ var astroConfigTemplate = (options) => {
3557
3557
  ${defineConfigImport}
3558
3558
  import mdx from "@astrojs/mdx";
3559
3559
  import tailwindcss from "@tailwindcss/vite";
3560
- import { blumeMarkdownProcessor, blumeMdxProcessor, blumeShikiTransformers } from "blume/markdown";
3561
- ${twoslashImport}${reactImport}${vueImport}${svelteImport}${blumeImport}${adapterImport}
3560
+ import { blumeMarkdownProcessor, blumeMdxProcessor, blumeShikiTransformers, blumeTwoslashTransformer } from "blume/markdown";
3561
+ ${reactImport}${vueImport}${svelteImport}${blumeImport}${adapterImport}
3562
3562
  export default defineConfig({
3563
3563
  root: ${JSON.stringify(context.outDir)},
3564
3564
  srcDir: ${JSON.stringify(`${context.outDir}/src`)},
@@ -3986,7 +3986,9 @@ import data from "blume:data";
3986
3986
  export const prerender = true;
3987
3987
 
3988
3988
  // Custom (non-content) pages opted into a generated card, baked in at build.
3989
- const customRoutes = ${JSON.stringify(customRoutes)};
3989
+ // The annotation keeps the empty-array case from being an implicit any[]
3990
+ // (ts(7034)) under a strict tsconfig.
3991
+ const customRoutes: { slug: string; title: string }[] = ${JSON.stringify(customRoutes)};
3990
3992
 
3991
3993
  export function getStaticPaths() {
3992
3994
  const seen = new Set<string>();
@@ -4651,9 +4653,16 @@ const htmlLang = i18n ? i18n.defaultLocale : "en";
4651
4653
  </PageLayout>
4652
4654
  `;
4653
4655
  var islandDirective = (spec) => spec.client === "only" ? `client:only="${spec.framework}"` : `client:${spec.client}`;
4656
+ var wrapperPropsType = (name) => `type Props = typeof ${name} extends (
4657
+ props: infer P extends object,
4658
+ ...rest: never[]
4659
+ ) => unknown
4660
+ ? P
4661
+ : Record<string, unknown>;`;
4654
4662
  var islandWrapperTemplate = (spec) => `---
4655
4663
  // Generated by Blume. Do not edit.
4656
4664
  import Island from ${JSON.stringify(spec.file)};
4665
+ ${wrapperPropsType("Island")}
4657
4666
  ---
4658
4667
  <Island ${islandDirective(spec)} {...Astro.props}><slot /></Island>
4659
4668
  `;
@@ -4684,6 +4693,7 @@ var exampleSlug = (path) => path.replaceAll(/[^a-zA-Z0-9]/gu, (char) => `_${(cha
4684
4693
  var exampleWrapperTemplate = (spec) => `---
4685
4694
  // Generated by Blume. Do not edit.
4686
4695
  import Example from ${JSON.stringify(spec.file)};
4696
+ ${wrapperPropsType("Example")}
4687
4697
  ---
4688
4698
  <Example ${exampleDirective(spec)}{...Astro.props}><slot /></Example>
4689
4699
  `;
@@ -4760,7 +4770,42 @@ const Example = entry.Component;
4760
4770
  <!-- Flex + margin:auto centers the example and, unlike place-items, keeps
4761
4771
  the top edge reachable when the example outgrows the frame. -->
4762
4772
  <body style="display:flex;min-height:100svh;padding:1.5rem">
4763
- <div style="margin:auto"><Example /></div>
4773
+ <div data-blume-example style="margin:auto"><Example /></div>
4774
+ <script is:inline>
4775
+ (() => {
4776
+ // Report the example's rendered height so the embedding docs page can
4777
+ // size the preview pane to the content. The wrapper is observed rather
4778
+ // than the body: the body stretches to the frame's own height, so it
4779
+ // would only echo the pane back. Direct opens have no distinct parent
4780
+ // and skip out; the frame is same-origin with the docs page (see the
4781
+ // theme sync above), so the origin is pinned on both ends.
4782
+ if (window.parent === window) {
4783
+ return;
4784
+ }
4785
+ const wrapper = document.querySelector("[data-blume-example]");
4786
+ if (!wrapper) {
4787
+ return;
4788
+ }
4789
+ // The body's padding frames the example; fold it into the report so
4790
+ // the parent can apply the number as-is. Read from the live value —
4791
+ // the user's examples.css is injected after Blume's defaults precisely
4792
+ // so their tokens win, so a root font-size override must be honored
4793
+ // rather than assuming 1.5rem is 48px.
4794
+ const bodyStyle = getComputedStyle(document.body);
4795
+ const paddingPx =
4796
+ parseFloat(bodyStyle.paddingTop) + parseFloat(bodyStyle.paddingBottom);
4797
+ new ResizeObserver(() => {
4798
+ window.parent.postMessage(
4799
+ {
4800
+ height:
4801
+ Math.ceil(wrapper.getBoundingClientRect().height) + paddingPx,
4802
+ type: "blume:example-height",
4803
+ },
4804
+ window.location.origin
4805
+ );
4806
+ }).observe(wrapper);
4807
+ })();
4808
+ </script>
4764
4809
  </body>
4765
4810
  </html>
4766
4811
  `;
@@ -7879,7 +7924,8 @@ var ogConfigSchema = z2.strictObject({
7879
7924
  enabled: z2.boolean().optional(),
7880
7925
  fonts: z2.array(ogFontSchema).optional(),
7881
7926
  logo: z2.string().optional(),
7882
- palette: ogPaletteSchema.optional()
7927
+ palette: ogPaletteSchema.optional(),
7928
+ titles: z2.record(z2.string(), z2.string()).optional()
7883
7929
  });
7884
7930
  var rssConfigSchema = z2.strictObject({
7885
7931
  enabled: z2.boolean().default(true),
@@ -7916,9 +7962,23 @@ var githubConfigSchema = z2.strictObject({
7916
7962
  owner: z2.string(),
7917
7963
  repo: z2.string()
7918
7964
  });
7965
+ var codeThemeSchema = z2.custom((value) => {
7966
+ if (typeof value === "string") {
7967
+ return true;
7968
+ }
7969
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
7970
+ return false;
7971
+ }
7972
+ const theme = value;
7973
+ const settingsValid = theme.settings === undefined || Array.isArray(theme.settings);
7974
+ const tokenColorsValid = theme.tokenColors === undefined || Array.isArray(theme.tokenColors);
7975
+ const colorsValid = theme.colors === undefined || typeof theme.colors === "object" && theme.colors !== null && !Array.isArray(theme.colors);
7976
+ const hasContent = theme.settings !== undefined || theme.tokenColors !== undefined || theme.colors !== undefined;
7977
+ return settingsValid && tokenColorsValid && colorsValid && hasContent;
7978
+ }, "Expected a Shiki theme name or custom theme object");
7919
7979
  var codeBlockThemeSchema = z2.strictObject({
7920
- dark: z2.string().default("github-dark"),
7921
- light: z2.string().default("github-light")
7980
+ dark: codeThemeSchema.default("github-dark"),
7981
+ light: codeThemeSchema.default("github-light")
7922
7982
  });
7923
7983
  var codeBlocksConfigSchema = z2.strictObject({
7924
7984
  theme: codeBlockThemeSchema.default({})
@@ -8383,19 +8443,23 @@ var ensureGroup = (parent, rawSegment) => {
8383
8443
  };
8384
8444
  var pageOrder = (page, filename) => {
8385
8445
  if (page.meta.sidebar.order !== undefined) {
8386
- return page.meta.sidebar.order;
8446
+ return { order: page.meta.sidebar.order, orderIsAuthored: true };
8387
8447
  }
8388
8448
  if (isIndexStem(filename.replace(extname(filename), ""))) {
8389
- return Number.NEGATIVE_INFINITY;
8449
+ return { order: Number.NEGATIVE_INFINITY, orderIsAuthored: false };
8390
8450
  }
8391
8451
  if (page.contentType === "changelog") {
8392
8452
  const iso = page.meta.date ?? page.meta.changelog?.date;
8393
8453
  const time = iso ? Date.parse(iso) : Number.NaN;
8394
8454
  if (!Number.isNaN(time)) {
8395
- return -time;
8455
+ return { order: -time, orderIsAuthored: false };
8396
8456
  }
8397
8457
  }
8398
- return numericOrder(filename);
8458
+ const order = numericOrder(filename);
8459
+ return {
8460
+ order,
8461
+ orderIsAuthored: page.contentType !== "changelog" && Number.isFinite(order)
8462
+ };
8399
8463
  };
8400
8464
  var metaKey = (path, metaPrefix) => {
8401
8465
  if (!metaPrefix) {
@@ -8416,6 +8480,9 @@ var applyFolderMeta = (group, folderMeta, sharedMeta, metaPrefix) => {
8416
8480
  const position = rank.get(child.key);
8417
8481
  if (position !== undefined) {
8418
8482
  child.order = position;
8483
+ if (child.kind === "page") {
8484
+ child.orderIsAuthored = true;
8485
+ }
8419
8486
  }
8420
8487
  }
8421
8488
  }
@@ -8426,7 +8493,56 @@ var applyFolderMeta = (group, folderMeta, sharedMeta, metaPrefix) => {
8426
8493
  }
8427
8494
  }
8428
8495
  };
8429
- var sortNodes = (nodes) => {
8496
+ var indexTitleMismatchDiagnostic = (page, folderPath, folderMeta, sharedMeta, metaPrefix) => {
8497
+ if (!page.meta.title || page.fallback || folderPath === "") {
8498
+ return;
8499
+ }
8500
+ const meta = folderMeta.get(metaKey(folderPath, metaPrefix)) ?? sharedMeta.get(folderPath);
8501
+ if (!meta?.title || meta.title === page.title) {
8502
+ return;
8503
+ }
8504
+ return {
8505
+ code: "BLUME_NAV_INDEX_TITLE_MISMATCH",
8506
+ file: page.sourcePath ?? page.id,
8507
+ message: `Index page "${page.navPath}" has title "${page.title}", but its folder's meta.title is "${meta.title}" — the sidebar shows the folder title while the page's own <title>/heading still say "${page.title}".`,
8508
+ severity: "warning",
8509
+ suggestion: `Update the page's frontmatter title to match ("${meta.title}"), or leave it if the divergence is intentional.`
8510
+ };
8511
+ };
8512
+ var isAuthoredOrder = (node) => node.kind === "group" || node.orderIsAuthored;
8513
+ var duplicateOrderDiagnostics = (nodes) => {
8514
+ const byOrder = new Map;
8515
+ for (const node of nodes) {
8516
+ if (!Number.isFinite(node.order) || !isAuthoredOrder(node)) {
8517
+ continue;
8518
+ }
8519
+ const tied = byOrder.get(node.order);
8520
+ if (tied) {
8521
+ tied.push(node);
8522
+ } else {
8523
+ byOrder.set(node.order, [node]);
8524
+ }
8525
+ }
8526
+ const diagnostics = [];
8527
+ for (const [order, tied] of byOrder) {
8528
+ if (tied.length > 1) {
8529
+ const names = tied.map((node) => `"${node.label}"`);
8530
+ const list2 = names.length > 2 ? `${names.slice(0, -1).join(", ")}, and ${names.at(-1)}` : names.join(" and ");
8531
+ const verb = tied.length > 2 ? "all have" : "both have";
8532
+ const file = tied.find((node) => node.kind === "page")?.file;
8533
+ diagnostics.push({
8534
+ code: "BLUME_DUPLICATE_SIDEBAR_ORDER",
8535
+ file,
8536
+ message: `${list2} ${verb} sidebar order ${order}; falling back to alphabetical order.`,
8537
+ severity: "warning",
8538
+ suggestion: "Give each item a distinct sidebar.order (or folder meta order)."
8539
+ });
8540
+ }
8541
+ }
8542
+ return diagnostics;
8543
+ };
8544
+ var sortNodes = (nodes, diagnostics) => {
8545
+ diagnostics.push(...duplicateOrderDiagnostics(nodes));
8430
8546
  nodes.sort((a, b) => {
8431
8547
  if (a.order !== b.order) {
8432
8548
  return a.order - b.order;
@@ -8435,7 +8551,7 @@ var sortNodes = (nodes) => {
8435
8551
  });
8436
8552
  for (const node of nodes) {
8437
8553
  if (node.kind === "group") {
8438
- sortNodes(node.children);
8554
+ sortNodes(node.children, diagnostics);
8439
8555
  }
8440
8556
  }
8441
8557
  };
@@ -8483,16 +8599,22 @@ var toNavNode = (node, display) => {
8483
8599
  path: node.routePath
8484
8600
  };
8485
8601
  };
8486
- var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, display, tabPaths) => {
8602
+ var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, display, tabPaths, diagnostics = []) => {
8487
8603
  const root = createGroup("", "", "", 0);
8488
8604
  for (const page of pages) {
8489
- if (page.meta.sidebar.hidden) {
8490
- continue;
8491
- }
8492
8605
  const parts = page.navPath.split("/");
8493
8606
  const filename = parts.at(-1) ?? page.navPath;
8494
8607
  const stem = filename.replace(extname(filename), "");
8495
8608
  const dirs = parts.slice(0, -1);
8609
+ if (isIndexStem(stem)) {
8610
+ const diagnostic = indexTitleMismatchDiagnostic(page, dirs.join("/"), folderMeta, sharedMeta, metaPrefix);
8611
+ if (diagnostic) {
8612
+ diagnostics.push(diagnostic);
8613
+ }
8614
+ }
8615
+ if (page.meta.sidebar.hidden) {
8616
+ continue;
8617
+ }
8496
8618
  const routeSegments = page.route.split("/").filter(Boolean);
8497
8619
  const folderParts = isIndexStem(stem) ? routeSegments : routeSegments.slice(0, -1);
8498
8620
  const routeDirCount = dirs.filter((dir) => !GROUP_FOLDER.test(dir)).length;
@@ -8506,21 +8628,24 @@ var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, display
8506
8628
  }
8507
8629
  parent.routePath ??= `/${folderParts.slice(0, consumed).join("/")}`;
8508
8630
  }
8631
+ const { order, orderIsAuthored } = pageOrder(page, filename);
8509
8632
  parent.children.push({
8510
8633
  badge: page.meta.sidebar.badge,
8511
8634
  deprecated: page.meta.deprecated || undefined,
8512
8635
  description: page.description,
8636
+ file: page.sourcePath,
8513
8637
  icon: page.meta.sidebar.icon,
8514
8638
  key: segmentKey(stem),
8515
8639
  kind: "page",
8516
8640
  label: page.meta.sidebar.label ?? page.title,
8517
- order: pageOrder(page, filename),
8641
+ order,
8642
+ orderIsAuthored,
8518
8643
  pageId: page.id,
8519
8644
  route: page.route
8520
8645
  });
8521
8646
  }
8522
8647
  applyFolderMeta(root, folderMeta, sharedMeta, metaPrefix);
8523
- sortNodes(root.children);
8648
+ sortNodes(root.children, diagnostics);
8524
8649
  hoistPages(root.children, display === "flat");
8525
8650
  hoistTabSections(root.children, tabPaths, display === "flat");
8526
8651
  return root.children.map((child) => toNavNode(child, display));
@@ -8662,19 +8787,21 @@ var buildNavigation = (pages, options) => {
8662
8787
  }
8663
8788
  }
8664
8789
  }
8790
+ const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
8665
8791
  if (options.sidebar) {
8666
8792
  const sidebar2 = buildConfigSidebar(options.sidebar, byRoute, display, basePath);
8667
8793
  return {
8668
8794
  featured,
8795
+ root: rootTabPath,
8669
8796
  selectors,
8670
8797
  sidebar: sidebar2,
8671
8798
  tabs: withTabHrefs(tabs, sidebar2)
8672
8799
  };
8673
8800
  }
8674
- const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
8675
- const sidebar = buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix, display, new Set(tabs.flatMap((tab) => tab.path === rootTabPath ? [] : [tab.path])));
8801
+ const sidebar = buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix, display, new Set(tabs.flatMap((tab) => tab.path === rootTabPath ? [] : [tab.path])), options.diagnostics);
8676
8802
  return {
8677
8803
  featured,
8804
+ root: rootTabPath,
8678
8805
  selectors,
8679
8806
  sidebar,
8680
8807
  tabs: withTabHrefs(tabs, sidebar)
@@ -8711,6 +8838,7 @@ var localePagesFor = (code, real, fallback, fallbackByKey, i18n, basePath) => {
8711
8838
  if (!present.has(key)) {
8712
8839
  filled.push({
8713
8840
  ...source,
8841
+ fallback: true,
8714
8842
  locale: code,
8715
8843
  route: withBasePath(basePath, localizeRoute(key, code, i18n))
8716
8844
  });
@@ -8718,7 +8846,7 @@ var localePagesFor = (code, real, fallback, fallbackByKey, i18n, basePath) => {
8718
8846
  }
8719
8847
  return [...real, ...filled];
8720
8848
  };
8721
- var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n) => {
8849
+ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n, diagnostics) => {
8722
8850
  const localizePath = (path) => path.startsWith("/") ? localizeRoute(path, code, i18n) : path;
8723
8851
  const tabs = options.navigation.tabs?.map((tab) => ({
8724
8852
  ...tab,
@@ -8732,6 +8860,7 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
8732
8860
  const localePages = localePagesFor(code, real, fallback, fallbackByKey, i18n, options.basePath ?? "");
8733
8861
  return buildNavigation(localePages, {
8734
8862
  basePath: options.basePath ?? "",
8863
+ diagnostics,
8735
8864
  display: options.navigation.sidebar.display,
8736
8865
  featured: options.navigation.featured,
8737
8866
  folderMeta: options.folderMeta,
@@ -8744,7 +8873,7 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
8744
8873
  tabs
8745
8874
  });
8746
8875
  };
8747
- var buildI18nNavigation = (pages, options, i18n) => {
8876
+ var buildI18nNavigation = (pages, options, i18n, diagnostics) => {
8748
8877
  const fallback = resolveFallbackLocale(i18n);
8749
8878
  const fallbackByKey = new Map;
8750
8879
  if (fallback) {
@@ -8755,8 +8884,19 @@ var buildI18nNavigation = (pages, options, i18n) => {
8755
8884
  }
8756
8885
  }
8757
8886
  const navigationByLocale = {};
8887
+ const seen = new Set;
8758
8888
  for (const { code } of i18n.locales) {
8759
- navigationByLocale[code] = buildLocaleNavigation(code, pages, fallback, fallbackByKey, options, i18n);
8889
+ const localeDiagnostics = [];
8890
+ navigationByLocale[code] = buildLocaleNavigation(code, pages, fallback, fallbackByKey, options, i18n, localeDiagnostics);
8891
+ for (const diagnostic of localeDiagnostics) {
8892
+ const key = `${diagnostic.code}
8893
+ ${diagnostic.file ?? ""}
8894
+ ${diagnostic.message}`;
8895
+ if (!seen.has(key)) {
8896
+ seen.add(key);
8897
+ diagnostics.push(diagnostic);
8898
+ }
8899
+ }
8760
8900
  }
8761
8901
  const navigation = navigationByLocale[i18n.defaultLocale] ?? {
8762
8902
  featured: [],
@@ -8769,9 +8909,10 @@ var buildI18nNavigation = (pages, options, i18n) => {
8769
8909
  var buildContentGraph = (pages, options) => {
8770
8910
  const { diagnostics, routes } = collectRoutes(pages);
8771
8911
  const { i18n } = options;
8772
- const { navigation, navigationByLocale } = i18n ? buildI18nNavigation(pages, options, i18n) : {
8912
+ const { navigation, navigationByLocale } = i18n ? buildI18nNavigation(pages, options, i18n, diagnostics) : {
8773
8913
  navigation: buildNavigation(pages, {
8774
8914
  basePath: options.basePath ?? "",
8915
+ diagnostics,
8775
8916
  display: options.navigation.sidebar.display,
8776
8917
  featured: options.navigation.featured,
8777
8918
  folderMeta: options.folderMeta,
@@ -9939,6 +10080,34 @@ var PER_PAGE = 100;
9939
10080
  var LEADING_V = /^v/iu;
9940
10081
  var NON_SLUG3 = /[^a-z0-9]+/gu;
9941
10082
  var EDGE_DASHES = /^-+|-+$/gu;
10083
+ var DESCRIPTION_MAX = 160;
10084
+ var DESCRIPTION_MIN = 110;
10085
+ var CODE_FENCE2 = /```[\s\S]*?```/gu;
10086
+ var HEADING_LINE = /^#{1,6}\s.*$/gmu;
10087
+ var LIST_MARK = /^\s*(?:[-*+]|\d+[.)])\s+/u;
10088
+ var CHANGESET_HASH = /^[0-9a-f]{7,40}:\s+/u;
10089
+ var IMAGE = /!\[[^\]]*\]\([^)]*\)/gu;
10090
+ var LINK = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
10091
+ var INLINE_CODE2 = /`(?<code>[^`]+)`/gu;
10092
+ var HTML_OR_JSX = /<\/?[a-zA-Z][^\n<>]*>|<\/?>/gu;
10093
+ var MARKDOWN_PUNCT = /[*_~>]+/gu;
10094
+ var WHITESPACE2 = /\s+/gu;
10095
+ var TRAILING_FRAGMENT = /[\s,;:.—–-]+$/u;
10096
+ var releaseDescription = (body) => {
10097
+ const text = body.replaceAll(CODE_FENCE2, " ").replaceAll(HEADING_LINE, "").split(`
10098
+ `).map((line) => line.replace(LIST_MARK, "").replace(CHANGESET_HASH, "")).join(`
10099
+ `).replaceAll(IMAGE, " ").replaceAll(LINK, "$<text>").replaceAll(INLINE_CODE2, "$<code>").replaceAll(HTML_OR_JSX, " ").replaceAll(MARKDOWN_PUNCT, " ").replaceAll(WHITESPACE2, " ").trim();
10100
+ if (!text) {
10101
+ return;
10102
+ }
10103
+ if (text.length <= DESCRIPTION_MAX) {
10104
+ return text;
10105
+ }
10106
+ const slice = text.slice(0, DESCRIPTION_MAX - 1);
10107
+ const boundary = slice.lastIndexOf(" ");
10108
+ const head = (boundary >= DESCRIPTION_MIN ? slice.slice(0, boundary) : slice).replace(TRAILING_FRAGMENT, "");
10109
+ return `${head}…`;
10110
+ };
9942
10111
  var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG3, "-").replaceAll(EDGE_DASHES, "");
9943
10112
  var githubHeaders = () => {
9944
10113
  const headers = new Headers({ Accept: "application/vnd.github+json" });
@@ -9956,9 +10125,11 @@ var releaseToEntry = (release) => {
9956
10125
  const body = (release.body ?? "").replaceAll(`\r
9957
10126
  `, `
9958
10127
  `).trim();
10128
+ const description = releaseDescription(body);
9959
10129
  const data = {
9960
10130
  changelog: { category, version },
9961
10131
  date,
10132
+ ...description ? { seo: { description } } : {},
9962
10133
  title,
9963
10134
  type: "changelog"
9964
10135
  };
@@ -10940,6 +11111,34 @@ var entryIdDiagnostics = (pages, collectionBase) => {
10940
11111
  }
10941
11112
  return diagnostics;
10942
11113
  };
11114
+ var normalizeLoadedEntries = (loaded, config) => {
11115
+ const frontmatterExtend = Object.keys(config.frontmatter.extend).length > 0 ? config.frontmatter.extend : undefined;
11116
+ const pages = [];
11117
+ const allDiagnostics = [];
11118
+ let droppedPages = 0;
11119
+ for (const { source, entries, diagnostics } of loaded) {
11120
+ allDiagnostics.push(...diagnostics);
11121
+ for (const entry of entries) {
11122
+ const normalized = normalizeEntry(entry, {
11123
+ basePath: config.basePath,
11124
+ defaultType: config.content.defaultType,
11125
+ frontmatterExtend,
11126
+ i18n: config.i18n,
11127
+ source: {
11128
+ name: source.name,
11129
+ prefix: source.prefix,
11130
+ staged: source.staged
11131
+ }
11132
+ });
11133
+ if (normalized.pages.length === 0 && normalized.diagnostics.length > 0) {
11134
+ droppedPages += 1;
11135
+ }
11136
+ pages.push(...normalized.pages);
11137
+ allDiagnostics.push(...normalized.diagnostics);
11138
+ }
11139
+ }
11140
+ return { diagnostics: allDiagnostics, droppedPages, pages };
11141
+ };
10943
11142
  var scanProject = async (root, options = {}) => {
10944
11143
  const mode = options.mode ?? "dev";
10945
11144
  const preview = options.preview ?? false;
@@ -10964,27 +11163,11 @@ var scanProject = async (root, options = {}) => {
10964
11163
  Promise.all(sources.map(async (source) => ({ source, ...await source.load() }))),
10965
11164
  discoverFolderMeta(metaSources, { localeDirs })
10966
11165
  ]);
10967
- const frontmatterExtend = Object.keys(config.frontmatter.extend).length > 0 ? config.frontmatter.extend : undefined;
10968
- const allPages = [];
10969
- const contentDiagnostics = [];
10970
- for (const { source, entries, diagnostics } of loaded) {
10971
- contentDiagnostics.push(...diagnostics);
10972
- for (const entry of entries) {
10973
- const normalized = normalizeEntry(entry, {
10974
- basePath: config.basePath,
10975
- defaultType: config.content.defaultType,
10976
- frontmatterExtend,
10977
- i18n: config.i18n,
10978
- source: {
10979
- name: source.name,
10980
- prefix: source.prefix,
10981
- staged: source.staged
10982
- }
10983
- });
10984
- allPages.push(...normalized.pages);
10985
- contentDiagnostics.push(...normalized.diagnostics);
10986
- }
10987
- }
11166
+ const {
11167
+ diagnostics: contentDiagnostics,
11168
+ droppedPages,
11169
+ pages: allPages
11170
+ } = normalizeLoadedEntries(loaded, config);
10988
11171
  const pages = mode === "build" && !preview ? allPages.filter((page) => !page.meta.draft) : allPages;
10989
11172
  const lastModified = resolveLastModifiedConfig(config.lastModified);
10990
11173
  if (lastModified.enabled && lastModified.source === "git") {
@@ -11016,6 +11199,7 @@ var scanProject = async (root, options = {}) => {
11016
11199
  ...graph.diagnostics,
11017
11200
  ...i18nWarnings
11018
11201
  ],
11202
+ droppedPages,
11019
11203
  graph,
11020
11204
  manifest,
11021
11205
  mode,
@@ -11390,15 +11574,15 @@ var parseYouTubeId = (input) => {
11390
11574
  };
11391
11575
 
11392
11576
  // src/ai/component-markdown.ts
11393
- var evaluateExpression = (raw) => {
11577
+ var evaluateExpression = (raw, frontmatter) => {
11394
11578
  try {
11395
- const value = new Function(`"use strict"; return (${raw});`)();
11579
+ const value = new Function("frontmatter", `"use strict"; return (${raw});`)(frontmatter);
11396
11580
  return { ok: true, value };
11397
11581
  } catch {
11398
11582
  return { ok: false, value: undefined };
11399
11583
  }
11400
11584
  };
11401
- var readProps = (node) => {
11585
+ var readProps = (node, frontmatter) => {
11402
11586
  const props = {};
11403
11587
  let lossy = false;
11404
11588
  for (const attribute of node.attributes ?? []) {
@@ -11411,7 +11595,7 @@ var readProps = (node) => {
11411
11595
  } else if (typeof attribute.value === "string") {
11412
11596
  props[attribute.name] = attribute.value;
11413
11597
  } else {
11414
- const result = evaluateExpression(attribute.value.value);
11598
+ const result = evaluateExpression(attribute.value.value, frontmatter);
11415
11599
  if (result.ok) {
11416
11600
  props[attribute.name] = result.value;
11417
11601
  } else {
@@ -11578,12 +11762,13 @@ var renderChildren2 = (walk, node) => {
11578
11762
  return dedent(spliced).trim();
11579
11763
  };
11580
11764
  var serializeElement = (serializer, walk, node) => serializer({
11581
- ...readProps(node),
11765
+ ...readProps(node, walk.frontmatter),
11582
11766
  childComponents: (name) => (node.children ?? []).filter((child) => isJsxElement(child) && child.name === name).map((child) => ({
11583
- ...readProps(child),
11767
+ ...readProps(child, walk.frontmatter),
11584
11768
  children: renderChildren2(walk, child)
11585
11769
  })),
11586
- children: renderChildren2(walk, node)
11770
+ children: renderChildren2(walk, node),
11771
+ frontmatter: walk.frontmatter ?? {}
11587
11772
  });
11588
11773
  var collectSplices = (walk, nodes, out) => {
11589
11774
  for (const node of nodes) {
@@ -11602,7 +11787,7 @@ var collectSplices = (walk, nodes, out) => {
11602
11787
  collectSplices(walk, node.children ?? [], out);
11603
11788
  }
11604
11789
  };
11605
- var downlevelComponents = (source, components) => {
11790
+ var downlevelComponents = (source, components, frontmatter) => {
11606
11791
  const custom = components && Object.keys(components).length > 0;
11607
11792
  const registry2 = custom ? { ...SERIALIZERS, ...components } : SERIALIZERS;
11608
11793
  const hint = custom ? componentHint(registry2) : BUILT_IN_HINT;
@@ -11616,7 +11801,7 @@ var downlevelComponents = (source, components) => {
11616
11801
  return source;
11617
11802
  }
11618
11803
  const splices = [];
11619
- collectSplices({ registry: registry2, source }, tree.children ?? [], splices);
11804
+ collectSplices({ frontmatter, registry: registry2, source }, tree.children ?? [], splices);
11620
11805
  return splices.length > 0 ? applySplices(source, splices) : source;
11621
11806
  };
11622
11807
 
@@ -11746,7 +11931,8 @@ var buildFull = async (project) => {
11746
11931
  const pages = eligiblePages(project).toSorted((a, b) => a.route.localeCompare(b.route));
11747
11932
  const sections = await Promise.all(pages.map(async (page) => {
11748
11933
  const raw = await readEntryText(project, page);
11749
- const body = downlevelComponents(applyAgentVisibility(frontmatter_default(raw).content), config.ai.markdownComponents).trim();
11934
+ const parsed = frontmatter_default(raw);
11935
+ const body = downlevelComponents(applyAgentVisibility(parsed.content), config.ai.markdownComponents, parsed.data).trim();
11750
11936
  const url = pageUrl(page.route, config.deployment.site, normalizeBasePath(config.deployment.base));
11751
11937
  return [`# ${page.title}`, `Source: ${url}`, "", body].join(`
11752
11938
  `);
@@ -11920,7 +12106,11 @@ var hasGeneratedChangelog = (project, userPages) => {
11920
12106
  return (hasChangelog || hasChangelogSource) && !routeIsTaken(userPages, project.graph.pages, "/changelog");
11921
12107
  };
11922
12108
  var humanizeSegment = (segment) => segment.split(/[-_]/u).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
11923
- var customOgRoutes = (pages, siteTitle) => {
12109
+ var customOgRoutes = (pages, siteTitle, titles = {}) => {
12110
+ const overrides = new Map(Object.entries(titles).map(([route, title]) => [
12111
+ `/${route.split("/").filter(Boolean).join("/")}`,
12112
+ title
12113
+ ]));
11924
12114
  const seen = new Set;
11925
12115
  const routes = [];
11926
12116
  const collectRoute = (pattern) => {
@@ -11934,7 +12124,10 @@ var customOgRoutes = (pages, siteTitle) => {
11934
12124
  }
11935
12125
  seen.add(slug);
11936
12126
  const last = segments.at(-1);
11937
- routes.push({ slug, title: last ? humanizeSegment(last) : siteTitle });
12127
+ routes.push({
12128
+ slug,
12129
+ title: overrides.get(`/${segments.join("/")}`) ?? (last ? humanizeSegment(last) : siteTitle)
12130
+ });
11938
12131
  };
11939
12132
  for (const { pattern } of pages) {
11940
12133
  collectRoute(pattern);
@@ -12006,25 +12199,25 @@ var buildSearchIndex = async (outDir) => {
12006
12199
  };
12007
12200
 
12008
12201
  // src/search/documents.ts
12009
- var CODE_FENCE2 = /```[\s\S]*?```/gu;
12010
- var INLINE_CODE2 = /`(?<code>[^`]+)`/gu;
12011
- var HTML_OR_JSX = /<\/?[a-zA-Z][^\n<>]*>|<\/?>/gu;
12012
- var IMAGE = /!\[[^\]]*\]\([^)]*\)/gu;
12013
- var LINK = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
12202
+ var CODE_FENCE3 = /```[\s\S]*?```/gu;
12203
+ var INLINE_CODE3 = /`(?<code>[^`]+)`/gu;
12204
+ var HTML_OR_JSX2 = /<\/?[a-zA-Z][^\n<>]*>|<\/?>/gu;
12205
+ var IMAGE2 = /!\[[^\]]*\]\([^)]*\)/gu;
12206
+ var LINK2 = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
12014
12207
  var HEADING_MARK = /^#{1,6}\s+/gmu;
12015
- var MARKDOWN_PUNCT = /[*_~>]+/gu;
12016
- var WHITESPACE2 = /\s+/gu;
12208
+ var MARKDOWN_PUNCT2 = /[*_~>]+/gu;
12209
+ var WHITESPACE3 = /\s+/gu;
12017
12210
  var toPlainText = (markdown) => {
12018
- const withoutBlocks = markdown.replaceAll(CODE_FENCE2, " ").replaceAll(IMAGE, " ").replaceAll(LINK, "$<text>");
12211
+ const withoutBlocks = markdown.replaceAll(CODE_FENCE3, " ").replaceAll(IMAGE2, " ").replaceAll(LINK2, "$<text>");
12019
12212
  const pieces = [];
12020
12213
  let cursor = 0;
12021
- for (const match of withoutBlocks.matchAll(INLINE_CODE2)) {
12214
+ for (const match of withoutBlocks.matchAll(INLINE_CODE3)) {
12022
12215
  const start = match.index ?? 0;
12023
- pieces.push(withoutBlocks.slice(cursor, start).replaceAll(HTML_OR_JSX, " "), match.groups?.code ?? "");
12216
+ pieces.push(withoutBlocks.slice(cursor, start).replaceAll(HTML_OR_JSX2, " "), match.groups?.code ?? "");
12024
12217
  cursor = start + match[0].length;
12025
12218
  }
12026
- pieces.push(withoutBlocks.slice(cursor).replaceAll(HTML_OR_JSX, " "));
12027
- return pieces.join("").replaceAll(HEADING_MARK, "").replaceAll(MARKDOWN_PUNCT, " ").replaceAll(WHITESPACE2, " ").trim();
12219
+ pieces.push(withoutBlocks.slice(cursor).replaceAll(HTML_OR_JSX2, " "));
12220
+ return pieces.join("").replaceAll(HEADING_MARK, "").replaceAll(MARKDOWN_PUNCT2, " ").replaceAll(WHITESPACE3, " ").trim();
12028
12221
  };
12029
12222
  var buildCrumbIndex = (sidebar) => {
12030
12223
  const index = new Map;
@@ -12346,6 +12539,7 @@ import {
12346
12539
  lstat,
12347
12540
  mkdir as mkdir6,
12348
12541
  readFile as readFile14,
12542
+ realpath,
12349
12543
  rename,
12350
12544
  rm as rm2,
12351
12545
  symlink,
@@ -12389,7 +12583,7 @@ var buildRawMarkdown = async (project) => {
12389
12583
  };
12390
12584
  const entries = await Promise.all(project.manifest.routes.map(async (route) => {
12391
12585
  const source = applyAgentVisibility(await readRoute(route));
12392
- const md = downlevelComponents(source, project.config.ai.markdownComponents);
12586
+ const md = downlevelComponents(source, project.config.ai.markdownComponents, frontmatter_default(source).data);
12393
12587
  const entry = md === source ? { mdx: source } : { md, mdx: source };
12394
12588
  return [route.path, entry];
12395
12589
  }));
@@ -14261,10 +14455,11 @@ var resolvedAstroPath = (fromDir) => {
14261
14455
  dir = parent;
14262
14456
  }
14263
14457
  };
14264
- var blumeDepsDir = (pkgDir = packageRoot()) => {
14265
- const candidates = [join26(pkgDir, "node_modules"), dirname9(pkgDir)];
14266
- return candidates.find((dir) => existsSync14(join26(dir, "astro"))) ?? null;
14267
- };
14458
+ var depsCandidates = (pkgDir) => [
14459
+ join26(pkgDir, "node_modules"),
14460
+ dirname9(pkgDir)
14461
+ ];
14462
+ var candidateHolding = (pkgDir, ...segments) => depsCandidates(pkgDir).find((dir) => existsSync14(join26(dir, ...segments))) ?? null;
14268
14463
  var linkDepsJunction = async (link, depsDir) => {
14269
14464
  let existing;
14270
14465
  try {
@@ -14298,18 +14493,43 @@ var astroConflictWarning = (blumeAstroPkg, shadowAstroPkg) => {
14298
14493
  const pin = blume ?? "<Blume's astro version>";
14299
14494
  return `Astro version conflict: another dependency hoisted ${versions} to the project root, so @astrojs/mdx binds to the wrong copy and the build fails on a missing export (e.g. "chunkToString"). A single symlink can't reconcile a split install — pin Blume's Astro by adding a package.json "overrides" (npm/bun/pnpm) or "resolutions" (yarn) entry { "astro": "${pin}" }, then reinstall. Run \`npm ls astro\` to find the dependency pulling the older copy.`;
14300
14495
  };
14496
+ var dropStaleDepsLink = async (link, pkgDir) => {
14497
+ let existing;
14498
+ try {
14499
+ existing = await lstat(link);
14500
+ } catch {
14501
+ return;
14502
+ }
14503
+ if (!existing.isSymbolicLink()) {
14504
+ return;
14505
+ }
14506
+ let linkedBlume;
14507
+ let runningBlume;
14508
+ try {
14509
+ linkedBlume = await realpath(join26(link, "blume"));
14510
+ runningBlume = await realpath(pkgDir);
14511
+ } catch {
14512
+ return;
14513
+ }
14514
+ if (linkedBlume !== runningBlume) {
14515
+ await rm2(link, { force: true });
14516
+ }
14517
+ };
14301
14518
  var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
14302
- const depsDir = blumeDepsDir(pkgDir);
14303
- if (!depsDir) {
14519
+ const astroDir = candidateHolding(pkgDir, "astro");
14520
+ if (!astroDir) {
14304
14521
  return null;
14305
14522
  }
14306
- const blumeAstro = resolveAstroPackageJson(depsDir);
14523
+ const mdxDir = candidateHolding(pkgDir, "@astrojs", "mdx");
14524
+ const blumeAstro = resolveAstroPackageJson(astroDir);
14525
+ await dropStaleDepsLink(join26(outDir, "node_modules"), pkgDir);
14307
14526
  const outDirAstro = resolvedAstroPath(outDir);
14308
- if (blumeAstro && outDirAstro === blumeAstro) {
14527
+ const astroCorrect = blumeAstro !== null && outDirAstro === blumeAstro;
14528
+ if (astroCorrect && mdxDir === astroDir) {
14309
14529
  return null;
14310
14530
  }
14311
- if (existsSync14(join26(depsDir, "@astrojs", "mdx"))) {
14312
- await linkDepsJunction(join26(outDir, "node_modules"), depsDir);
14531
+ if (mdxDir && (mdxDir === astroDir || astroCorrect)) {
14532
+ await linkDepsJunction(join26(outDir, "node_modules"), mdxDir);
14313
14533
  return null;
14314
14534
  }
14315
14535
  return astroConflictWarning(blumeAstro, outDirAstro);
@@ -14807,7 +15027,7 @@ var generateRuntime = async (project) => {
14807
15027
  const needsVue = frameworks.has("vue");
14808
15028
  const needsSvelte = frameworks.has("svelte");
14809
15029
  const reactCompilerPath = resolveReactCompiler(config, needsReact);
14810
- const ogRoutes = customOgRoutes(pages, config.title);
15030
+ const ogRoutes = customOgRoutes(pages, config.title, config.seo.og.titles);
14811
15031
  const mcp = planMcp(project, srcDir, pages);
14812
15032
  pages.push(...mcp.discoveryPages);
14813
15033
  const hasStaged = staged.size > 0;
@@ -15104,12 +15324,13 @@ var prepareProject = async (options) => {
15104
15324
  }
15105
15325
  }
15106
15326
  const hadErrors = reportDiagnostics(project.diagnostics, options.root);
15327
+ const dropped = project.droppedPages > 0 ? `${project.droppedPages} page(s) failed frontmatter validation and were dropped from the site. ` : "";
15107
15328
  if (hadErrors && options.strict) {
15108
- logger.error("Aborting due to errors (strict mode).");
15329
+ logger.error(`Aborting due to errors. ${dropped}Fix the diagnostics above, or pass --no-strict to continue despite them.`);
15109
15330
  process.exit(1);
15110
15331
  }
15111
15332
  if (hasErrors(project.diagnostics) && !options.strict) {
15112
- logger.warn("Continuing despite errors. Use --strict to fail the build.");
15333
+ logger.warn(`Continuing despite errors. ${dropped}Use --strict to fail instead.`);
15113
15334
  }
15114
15335
  const { warnings } = await generateRuntime(project);
15115
15336
  for (const warning of warnings) {
@@ -15307,6 +15528,9 @@ var publishBuildArtifacts = async (project, distDir, args) => {
15307
15528
  ].join(`
15308
15529
  `));
15309
15530
  await runClientAssetChecks(distDir, args);
15531
+ if (project.droppedPages > 0) {
15532
+ logger.warn(`${project.droppedPages} page(s) failed frontmatter validation and are missing from this build.`);
15533
+ }
15310
15534
  logger.success(`Built to ${distDir}`);
15311
15535
  };
15312
15536
  var buildCommand = defineCommand3({
@@ -15343,7 +15567,11 @@ var buildCommand = defineCommand3({
15343
15567
  description: "Include drafts and unpublished CMS content.",
15344
15568
  type: "boolean"
15345
15569
  },
15346
- strict: { description: "Fail on diagnostics.", type: "boolean" }
15570
+ strict: {
15571
+ default: true,
15572
+ description: "Fail on error diagnostics (default; pass --no-strict to build anyway, dropping pages that fail validation).",
15573
+ type: "boolean"
15574
+ }
15347
15575
  },
15348
15576
  meta: {
15349
15577
  description: "Build the docs site for production.",
@@ -16032,7 +16260,7 @@ var eject = async (root) => {
16032
16260
  }
16033
16261
  if (config.seo.og.enabled) {
16034
16262
  files.push({
16035
- content: ogEndpointTemplate(customOgRoutes(pages, config.title)),
16263
+ content: ogEndpointTemplate(customOgRoutes(pages, config.title, config.seo.og.titles)),
16036
16264
  path: join31(srcDir, "pages", "og", "[...slug].png.ts")
16037
16265
  });
16038
16266
  }
@@ -17071,5 +17299,5 @@ process.on("unhandledRejection", (error) => {
17071
17299
  });
17072
17300
  runMain(main);
17073
17301
 
17074
- //# debugId=2D51E90E387F80A664756E2164756E21
17302
+ //# debugId=7D02660AA9B8797064756E2164756E21
17075
17303
  //# sourceMappingURL=index.js.map