@publish.os/nextjs 0.0.1-alpha.91 → 0.0.1-alpha.92

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 (153) hide show
  1. package/compiler-assets/nextjs/defaults/group/index.tsx +16 -11
  2. package/compiler-assets/nextjs/defaults/group/layout.tsx +6 -2
  3. package/compiler-assets/nextjs/defaults/group/page.tsx +7 -6
  4. package/compiler-assets/nextjs/defaults/project/next.config.mjs +2 -1
  5. package/compiler-assets/nextjs/defaults/root/index.tsx +16 -11
  6. package/compiler-assets/nextjs/defaults/root/layout.tsx +7 -3
  7. package/compiler-assets/nextjs/shims/agent-user-agents.ts +65 -0
  8. package/compiler-assets/nextjs/shims/document.tsx +9 -1
  9. package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +3 -0
  10. package/compiler-assets/nextjs/shims/proxy.ts +156 -54
  11. package/dist/{build-operation-5QMLXF7N.js → build-operation-CJDUL4OW.js} +5 -5
  12. package/dist/{chunk-GBPLER5H.js → chunk-3T4HVDSC.js} +2 -2
  13. package/dist/{chunk-UYGCOWRF.js → chunk-DX7PXFCM.js} +860 -50
  14. package/dist/{chunk-F3AGNDF4.js → chunk-DZ3VWEV5.js} +860 -575
  15. package/dist/{chunk-OXOFRXEI.js → chunk-NHSLTARU.js} +1284 -1164
  16. package/dist/{chunk-ORIUWJBQ.js → chunk-SBMDONTV.js} +3 -2
  17. package/dist/compiler.js +42 -23
  18. package/dist/components/blocks/agent-actions.d.ts +7 -7
  19. package/dist/components/blocks/agent-actions.js +1 -32
  20. package/dist/components/blocks/toc.d.ts +7 -7
  21. package/dist/components/ui/button.d.ts +1 -2
  22. package/dist/{compose-CVBJRAF6.js → compose-44GJSMXD.js} +2 -2
  23. package/dist/config/createNextConfig.js +2 -0
  24. package/dist/{dev-operation-OSHHWRWK.js → dev-operation-WNECBHZ5.js} +17 -7
  25. package/dist/{ide-support-CU6Z5PVR.js → ide-support-TG74L4FF.js} +3 -3
  26. package/dist/index.d.ts +143 -126
  27. package/dist/index.js +25 -0
  28. package/dist/kernel/components/blocks/AgentActions.d.ts +7 -7
  29. package/dist/kernel/components/blocks/AgentActions.js +1 -32
  30. package/dist/kernel/components/blocks/agent-actions.d.ts +1 -2
  31. package/dist/kernel/components/blocks/agent-actions.js +1 -32
  32. package/dist/kernel/components/blocks/animated-icon-swap.d.ts +2 -2
  33. package/dist/kernel/components/blocks/color-palette.d.ts +2 -2
  34. package/dist/kernel/components/blocks/logo-block.d.ts +2 -2
  35. package/dist/kernel/components/blocks/nav-breadcrumb.d.ts +6 -5
  36. package/dist/kernel/components/blocks/nav-breadcrumb.js +19 -13
  37. package/dist/kernel/components/blocks/navbar.d.ts +5 -4
  38. package/dist/kernel/components/blocks/navbar.js +62 -42
  39. package/dist/kernel/components/blocks/navigation-progress.d.ts +2 -2
  40. package/dist/kernel/components/blocks/next-prev.d.ts +4 -4
  41. package/dist/kernel/components/blocks/next-prev.js +29 -6
  42. package/dist/kernel/components/blocks/page-list.d.ts +4 -4
  43. package/dist/kernel/components/blocks/page-list.js +30 -7
  44. package/dist/kernel/components/blocks/search.d.ts +9 -15
  45. package/dist/kernel/components/blocks/sidebar-node.d.ts +3 -5
  46. package/dist/kernel/components/blocks/sidebar-node.js +12 -2
  47. package/dist/kernel/components/blocks/sidebar.d.ts +6 -7
  48. package/dist/kernel/components/blocks/sidebar.js +132 -26
  49. package/dist/kernel/components/blocks/theme-toggle.d.ts +3 -4
  50. package/dist/kernel/components/blocks/toc-client.d.ts +7 -7
  51. package/dist/kernel/components/blocks/toc.d.ts +3 -3
  52. package/dist/kernel/components/mdx/components/blockquote.d.ts +2 -2
  53. package/dist/kernel/components/mdx/components/callout.d.ts +3 -3
  54. package/dist/kernel/components/mdx/components/code-block.d.ts +2 -2
  55. package/dist/kernel/components/mdx/components/copy-code-button.d.ts +2 -2
  56. package/dist/kernel/components/mdx/components/figure-caption.d.ts +2 -2
  57. package/dist/kernel/components/mdx/components/figure.d.ts +3 -3
  58. package/dist/kernel/components/mdx/components/footnotes.d.ts +2 -2
  59. package/dist/kernel/components/mdx/components/heading-copy-link.d.ts +2 -2
  60. package/dist/kernel/components/mdx/components/heading.d.ts +2 -2
  61. package/dist/kernel/components/mdx/components/hr.d.ts +4 -4
  62. package/dist/kernel/components/mdx/components/image-zoom.d.ts +3 -3
  63. package/dist/kernel/components/mdx/components/image-zoom.js +3 -2
  64. package/dist/kernel/components/mdx/components/image.d.ts +2 -2
  65. package/dist/kernel/components/mdx/components/image.js +1 -1
  66. package/dist/kernel/components/mdx/components/inline-code.d.ts +2 -2
  67. package/dist/kernel/components/mdx/components/kbd.d.ts +3 -3
  68. package/dist/kernel/components/mdx/components/link.d.ts +3 -3
  69. package/dist/kernel/components/mdx/components/paragraph.d.ts +2 -2
  70. package/dist/kernel/components/mdx/components/strikethrough.d.ts +2 -2
  71. package/dist/kernel/components/mdx/components/table.d.ts +7 -7
  72. package/dist/kernel/components/mdx/components/task-list.d.ts +3 -3
  73. package/dist/kernel/components/mdx/components/video-player.d.ts +2 -2
  74. package/dist/kernel/components/mdx/components/video.d.ts +2 -3
  75. package/dist/kernel/components/mdx/error-boundary.d.ts +2 -3
  76. package/dist/kernel/components/mdx/error-fallback.d.ts +2 -2
  77. package/dist/kernel/components/mdx/mdx.d.ts +42 -11
  78. package/dist/kernel/components/mdx/mdx.js +1 -1
  79. package/dist/kernel/components/next-image.d.ts +3 -3
  80. package/dist/kernel/components/ui/accordion.d.ts +7 -7
  81. package/dist/kernel/components/ui/breadcrumb-trail.d.ts +2 -3
  82. package/dist/kernel/components/ui/breadcrumb.d.ts +13 -13
  83. package/dist/kernel/components/ui/button-group.d.ts +5 -5
  84. package/dist/kernel/components/ui/button.d.ts +3 -3
  85. package/dist/kernel/components/ui/card.d.ts +7 -7
  86. package/dist/kernel/components/ui/dialog.d.ts +10 -10
  87. package/dist/kernel/components/ui/drawer.d.ts +12 -12
  88. package/dist/kernel/components/ui/kbd.d.ts +3 -3
  89. package/dist/kernel/components/ui/link.d.ts +3 -3
  90. package/dist/kernel/components/ui/menu.d.ts +22 -22
  91. package/dist/kernel/components/ui/mod-key.d.ts +2 -3
  92. package/dist/kernel/components/ui/navigation-menu.d.ts +15 -15
  93. package/dist/kernel/components/ui/popover.d.ts +11 -11
  94. package/dist/kernel/components/ui/progress.d.ts +7 -7
  95. package/dist/kernel/components/ui/scroll-area.d.ts +1 -2
  96. package/dist/kernel/components/ui/select.d.ts +16 -16
  97. package/dist/kernel/components/ui/separator.d.ts +3 -3
  98. package/dist/kernel/components/ui/table.d.ts +7 -7
  99. package/dist/kernel/components/ui/tabs.d.ts +12 -12
  100. package/dist/kernel/components/ui/tag.d.ts +3 -3
  101. package/dist/kernel/components/ui/text.d.ts +4 -4
  102. package/dist/kernel/components/ui/toast.d.ts +11 -11
  103. package/dist/kernel/components/ui/tooltip.d.ts +9 -9
  104. package/dist/kernel/defaults.registry.d.ts +2 -2
  105. package/dist/kernel/defaults.registry.js +2 -2
  106. package/dist/kernel/routes/agent/markdown.d.ts +4 -0
  107. package/dist/kernel/routes/agent/markdown.js +11 -7
  108. package/dist/kernel/routes/layouts/group.d.ts +1 -1
  109. package/dist/kernel/routes/metadata/feed.d.ts +1 -2
  110. package/dist/kernel/routes/metadata/feed.js +34 -29
  111. package/dist/kernel/routes/metadata/llms.d.ts +3 -31
  112. package/dist/kernel/routes/metadata/llms.js +2 -200
  113. package/dist/kernel/routes/metadata/robots.d.ts +5 -3
  114. package/dist/kernel/routes/metadata/robots.js +4 -25
  115. package/dist/kernel/routes/metadata/sitemap.d.ts +15 -1
  116. package/dist/kernel/routes/metadata/sitemap.js +200 -112
  117. package/dist/kernel/routes/not-found.js +1 -1
  118. package/dist/kernel/routes/pages/group.d.ts +3 -3
  119. package/dist/kernel/routes/pages/group.js +10 -4
  120. package/dist/kernel/routes/pages/leaf.d.ts +9 -4
  121. package/dist/kernel/routes/pages/leaf.js +16 -8
  122. package/dist/kernel/routes/pages/root.d.ts +3 -3
  123. package/dist/kernel/routes/pages/root.js +20 -5
  124. package/dist/keyboard-shortcuts.d.ts +2 -2
  125. package/dist/manifest-types-CcVPH_jh.d.ts +83 -0
  126. package/dist/resolvers/manifest-resolver.d.ts +2 -2
  127. package/dist/resolvers/manifest-resolver.js +65 -35
  128. package/dist/search.d.ts +2 -9
  129. package/dist/search.js +2 -7
  130. package/dist/sidebar-types-CMrMM0Nj.d.ts +105 -0
  131. package/dist/theme-scope.d.ts +2 -2
  132. package/dist/types-BbqmOzLC.d.ts +168 -0
  133. package/dist/types-TjH1ek0X.d.ts +22 -0
  134. package/dist/types.d.ts +116 -91
  135. package/dist/{validate-local-source-SV44DBQT.js → validate-local-source-EN2GW4FR.js} +3 -3
  136. package/dist/{validate-publish-os-source-NZJF3TB2.js → validate-publish-os-source-OEWQDB7B.js} +2 -2
  137. package/init/seed/minimal/publish.os/blog/index.tsx +25 -22
  138. package/init/seed/minimal/publish.os/blog/page.tsx +31 -16
  139. package/init/seed/minimal/publish.os/components/blog/copy-link.tsx +1 -1
  140. package/init/seed/minimal/publish.os/components/blog/frontmatter.ts +41 -0
  141. package/init/seed/minimal/publish.os/publish.os.config.json +13 -2
  142. package/init/seed/minimal/seed.json +1 -0
  143. package/init/seed/scratch/publish.os/publish.os.config.json +1 -1
  144. package/package.json +4 -4
  145. package/release/app/phosphor/package.json +1 -1
  146. package/release/app/phosphor/pnpm-lock.yaml.template +53 -127
  147. package/release/compiler/pnpm-lock.yaml.template +49 -123
  148. package/release/release.json +4 -4
  149. package/dist/sidebar-types-DKpR27II.d.ts +0 -25
  150. package/dist/types-CJWRG05y.d.ts +0 -76
  151. package/dist/types-CUtrppxt.d.ts +0 -69
  152. package/dist/types-J2w_puPF.d.ts +0 -128
  153. package/dist/{button-BdCScEKi.d.ts → button-D6Xp2HBm.d.ts} +1 -1
@@ -7,8 +7,11 @@ import {
7
7
  ROOT_CONTENT_GROUP_KEY,
8
8
  admitThemeImageResource,
9
9
  assertSourceDependency,
10
+ authoredDate,
11
+ convertMdxToMarkdown,
10
12
  faviconsAndLogosManifestModule,
11
13
  groupedMdxExports,
14
+ mapSourceAssetUrl,
12
15
  mdxComponentExportName,
13
16
  mdxIconPackage,
14
17
  mediaAssetBudgets,
@@ -20,7 +23,7 @@ import {
20
23
  replacedMdxSourceFiles,
21
24
  supportedRuntimeComponentAliases,
22
25
  writeMediaOutputs
23
- } from "./chunk-UYGCOWRF.js";
26
+ } from "./chunk-DX7PXFCM.js";
24
27
  import {
25
28
  FRAMEWORK_PREFIX,
26
29
  FRAMEWORK_PROXY_MATCHER,
@@ -90,40 +93,6 @@ import ts3 from "typescript";
90
93
  // src/lib/assets/map-source-asset-css.ts
91
94
  import postcss from "postcss";
92
95
  import valueParser from "postcss-value-parser";
93
-
94
- // src/lib/assets/map-source-asset-url.ts
95
- var SOURCE_ASSET_PATH = "/assets/";
96
- function hasUnsafeControlCharacters(value) {
97
- for (let character of value) {
98
- let code = character.codePointAt(0) ?? 0;
99
- if (code <= 31 || code >= 127 && code <= 159) return !0;
100
- }
101
- return !1;
102
- }
103
- function assertSafeAssetPath(path) {
104
- if (path.includes("\\") || hasUnsafeControlCharacters(path))
105
- throw new TypeError("Local asset URLs must use safe path segments.");
106
- for (let segment of path.split("/")) {
107
- if (!segment)
108
- throw new TypeError("Local asset URLs must use safe path segments.");
109
- let decoded;
110
- try {
111
- decoded = decodeURIComponent(segment);
112
- } catch {
113
- throw new TypeError("Local asset URLs must use valid URL encoding.");
114
- }
115
- if (decoded === "." || decoded === ".." || decoded.includes("/") || decoded.includes("\\") || hasUnsafeControlCharacters(decoded))
116
- throw new TypeError("Local asset URLs must use safe path segments.");
117
- }
118
- }
119
- function mapSourceAssetUrl(value) {
120
- if (typeof value != "string" || !value.startsWith(SOURCE_ASSET_PATH))
121
- return value;
122
- let suffixIndex = value.search(/[?#]/u), pathname = suffixIndex < 0 ? value : value.slice(0, suffixIndex), suffix = suffixIndex < 0 ? "" : value.slice(suffixIndex), path = pathname.slice(SOURCE_ASSET_PATH.length);
123
- return assertSafeAssetPath(path), `${SOURCE_ASSET_PREFIX}/${path}${suffix}`;
124
- }
125
-
126
- // src/lib/assets/map-source-asset-css.ts
127
96
  var IMAGE_SET_FUNCTIONS = /* @__PURE__ */ new Set(["image-set", "-webkit-image-set"]);
128
97
  function mapCssUrls(value) {
129
98
  let parsed = valueParser(value);
@@ -250,12 +219,12 @@ async function readRegularFile(path, maximumBytes) {
250
219
  let entry = await file.stat();
251
220
  if (!entry.isFile() || entry.nlink !== 1 || entry.size > maximumBytes || entry.ino !== original.ino || entry.dev !== original.dev)
252
221
  throw new Error("A cached font asset is not a bounded regular file");
253
- let bytes = Buffer.alloc(entry.size + 1), offset = 0;
254
- for (; offset < bytes.length; ) {
222
+ let bytes2 = Buffer.alloc(entry.size + 1), offset = 0;
223
+ for (; offset < bytes2.length; ) {
255
224
  let { bytesRead } = await file.read(
256
- bytes,
225
+ bytes2,
257
226
  offset,
258
- bytes.length - offset,
227
+ bytes2.length - offset,
259
228
  offset
260
229
  );
261
230
  if (!bytesRead) break;
@@ -264,7 +233,7 @@ async function readRegularFile(path, maximumBytes) {
264
233
  let current = await lstat(path);
265
234
  if (offset !== entry.size || current.isSymbolicLink() || current.ino !== entry.ino || current.dev !== entry.dev || current.size !== entry.size || current.mtimeMs !== entry.mtimeMs || current.ctimeMs !== entry.ctimeMs)
266
235
  throw new Error("A cached font asset changed during verification");
267
- return bytes.subarray(0, offset);
236
+ return bytes2.subarray(0, offset);
268
237
  } finally {
269
238
  await file.close();
270
239
  }
@@ -403,7 +372,7 @@ async function readCached({
403
372
  throw new Error(
404
373
  "The cached font manifest does not match the selected release and request"
405
374
  );
406
- let files = [], bytes = 0;
375
+ let files = [], bytes2 = 0;
407
376
  for (let file of manifest.files) {
408
377
  if (!/^[a-f0-9]{64}$/.test(file.sha256) || !["woff2", "woff", "ttf", "otf", "svg"].includes(file.format))
409
378
  throw new Error("A cached font filename is invalid");
@@ -412,7 +381,7 @@ async function readCached({
412
381
  join2(directory, `${file.sha256}.${file.format}`),
413
382
  request.profile === "emoji" ? maximumEmojiBytes : maximumFontBytes
414
383
  );
415
- if (bytes += data.length, bytes > maximumTotalBytes)
384
+ if (bytes2 += data.length, bytes2 > maximumTotalBytes)
416
385
  throw new Error("The cached font response exceeds its size limit");
417
386
  if (data.length !== file.bytes || digest(data) !== file.sha256 || fontFileFormat(data, request.profile) !== file.format)
418
387
  throw new Error("The cached font bytes do not match their integrity");
@@ -559,7 +528,7 @@ async function prepareFontAssets({
559
528
  request,
560
529
  kind: "css",
561
530
  signal: requestSignal
562
- })).toString("utf8"), urls = request.profile === "emoji" ? [request.url] : fontUrls(css), files = [], bytes = 0;
531
+ })).toString("utf8"), urls = request.profile === "emoji" ? [request.url] : fontUrls(css), files = [], bytes2 = 0;
563
532
  for (let url of urls) {
564
533
  let data = await download({
565
534
  url,
@@ -567,7 +536,7 @@ async function prepareFontAssets({
567
536
  kind: request.profile === "emoji" ? "emoji" : "font",
568
537
  signal: requestSignal
569
538
  });
570
- if (bytes += data.length, bytes > maximumTotalBytes)
539
+ if (bytes2 += data.length, bytes2 > maximumTotalBytes)
571
540
  throw new Error("The font response exceeds its total size limit");
572
541
  files.push({
573
542
  url,
@@ -643,692 +612,12 @@ function localFontCacheDirectory() {
643
612
  return join3(root, "assets", "fonts");
644
613
  }
645
614
 
646
- // src/lib/compiler/markdown/convert-mdx-to-markdown.ts
647
- import remarkGfm from "remark-gfm";
648
- import remarkMdx from "remark-mdx";
649
- import remarkParse from "remark-parse";
650
- import remarkStringify from "remark-stringify";
651
- import { unified } from "unified";
652
-
653
- // src/lib/compiler/markdown/utils.ts
654
- function text(value) {
655
- return { type: "text", value };
656
- }
657
- function paragraph(children) {
658
- if (children.length !== 0)
659
- return { type: "paragraph", children };
660
- }
661
- function paragraphBlock(children) {
662
- let node = paragraph(children);
663
- return node ? [node] : [];
664
- }
665
- function strongLabel(value) {
666
- return { type: "strong", children: [text(value)] };
667
- }
668
- function inlineCode(value) {
669
- return { type: "inlineCode", value };
670
- }
671
- function normalizeComponentName(name) {
672
- return (name ?? "").trim().toLowerCase();
673
- }
674
- function addDiagnostic(context, diagnostic) {
675
- context.collectDiagnostics && context.diagnostics.push(diagnostic);
676
- }
677
- function getAttribute(node, name, context) {
678
- let attribute = node.attributes?.find(
679
- (candidate) => candidate.name === name
680
- );
681
- if (attribute) {
682
- if (attribute.value === null || attribute.value === void 0) return "true";
683
- if (typeof attribute.value == "string") return attribute.value;
684
- if (attribute.value.type === "mdxJsxAttributeValueExpression")
685
- return decodeExpressionValue(attribute.value.value, name, context);
686
- addDiagnostic(context, {
687
- code: "mdx-invalid-attribute",
688
- componentName: node.name ?? void 0,
689
- message: `Unsupported MDX attribute value for "${name}".`
690
- });
691
- }
692
- }
693
- function firstAttribute(node, names, context) {
694
- for (let name of names) {
695
- let value = getAttribute(node, name, context);
696
- if (value?.trim()) return value.trim();
697
- }
698
- }
699
- function decodeExpressionValue(value, attributeName, context) {
700
- let trimmed = value.trim();
701
- if (trimmed) {
702
- if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'"))
703
- try {
704
- return JSON.parse(
705
- trimmed.startsWith("'") ? `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"` : trimmed
706
- );
707
- } catch {
708
- return trimmed.slice(1, -1);
709
- }
710
- return trimmed.startsWith("`") && trimmed.endsWith("`") ? trimmed.slice(1, -1).replace(/\\`/g, "`").replace(/\\n/g, `
711
- `).replace(/\\t/g, " ") : (addDiagnostic(context, {
712
- code: "mdx-invalid-attribute",
713
- message: `MDX expression attribute "${attributeName}" was converted to literal text.`
714
- }), trimmed);
715
- }
716
- }
717
- function extractText(nodes) {
718
- return nodes.map((node) => {
719
- if (node.type === "break") return `
720
- `;
721
- if (node.type === "html" || node.type === "yaml") return "";
722
- if ("value" in node && typeof node.value == "string")
723
- return node.type === "code" ? `${node.value}
724
- ` : node.value;
725
- if ("alt" in node && typeof node.alt == "string") return node.alt;
726
- if ("children" in node && Array.isArray(node.children)) {
727
- let value = extractText(node.children);
728
- return [
729
- "emphasis",
730
- "strong",
731
- "delete",
732
- "link",
733
- "linkReference"
734
- ].includes(node.type) ? value : `${value}
735
- `;
736
- }
737
- return "";
738
- }).join("").trim();
739
- }
740
- function asPhrasing(nodes) {
741
- return nodes.flatMap((node) => {
742
- if (node.type === "paragraph") return node.children;
743
- if (node.type === "text" || node.type === "emphasis" || node.type === "strong" || node.type === "delete" || node.type === "inlineCode" || node.type === "break" || node.type === "link" || node.type === "image" || node.type === "html" || node.type === "footnoteReference")
744
- return [node];
745
- let label = extractText([node]);
746
- return label ? [text(label)] : [];
747
- });
748
- }
749
- function isBlockNode(node) {
750
- return node.type === "paragraph" || node.type === "heading" || node.type === "thematicBreak" || node.type === "blockquote" || node.type === "list" || node.type === "code" || node.type === "html" || node.type === "yaml" || node.type === "definition" || node.type === "footnoteDefinition" || node.type === "table";
751
- }
752
- function asBlocks(nodes) {
753
- let blocks = [], pendingPhrasing = [];
754
- for (let node of nodes)
755
- if (isBlockNode(node)) {
756
- let pendingParagraph2 = paragraph(pendingPhrasing);
757
- pendingParagraph2 && blocks.push(pendingParagraph2), pendingPhrasing = [], blocks.push(node);
758
- } else
759
- pendingPhrasing.push(node);
760
- let pendingParagraph = paragraph(pendingPhrasing);
761
- return pendingParagraph && blocks.push(pendingParagraph), blocks;
762
- }
763
- function handlerDiagnostic(node, context) {
764
- addDiagnostic(context, {
765
- code: "mdx-component-converted",
766
- componentName: node.name ?? void 0,
767
- message: `Converted MDX component "${node.name ?? "unknown"}" to Markdown.`
768
- });
769
- }
770
-
771
- // src/lib/compiler/markdown/handlers/callout.ts
772
- function calloutHandler(node, context, kind) {
773
- handlerDiagnostic(node, context);
774
- let title = firstAttribute(node, ["title", "label", "type"], context);
775
- if (kind === "inline") {
776
- let children2 = context.transformChildrenAsPhrasing(node, context);
777
- return [
778
- ...title ? [strongLabel(`${title}:`), text(" ")] : [],
779
- ...children2
780
- ];
781
- }
782
- let children = [];
783
- return title && children.push({ type: "paragraph", children: [strongLabel(title)] }), children.push(
784
- ...context.transformChildrenAsBlocks(node, context)
785
- ), children.length > 0 ? [{ type: "blockquote", children }] : [];
786
- }
787
-
788
- // src/lib/compiler/markdown/handlers/code-block.ts
789
- function codeBlockHandler(node, context) {
790
- handlerDiagnostic(node, context);
791
- let code = firstAttribute(node, ["code", "value"], context), language = firstAttribute(node, ["language", "lang"], context), title = firstAttribute(node, ["title"], context);
792
- return code !== void 0 ? [{
793
- type: "code",
794
- lang: language,
795
- meta: title === void 0 ? void 0 : `title="${title.replaceAll('"', "&quot;")}"`,
796
- value: code
797
- }] : context.transformChildrenAsBlocks(node, context);
798
- }
799
- function tabHandler(node, context) {
800
- handlerDiagnostic(node, context);
801
- let label = firstAttribute(
802
- node,
803
- ["title", "label", "value", "name"],
804
- context
805
- ), children = context.transformChildrenAsBlocks(node, context);
806
- return label ? [
807
- { type: "paragraph", children: [strongLabel(`Tab: ${label}`)] },
808
- ...children
809
- ] : children;
810
- }
811
-
812
- // src/lib/compiler/markdown/handlers/html.ts
813
- function inlineHtmlHandler(node, context) {
814
- handlerDiagnostic(node, context);
815
- let children = context.transformChildrenAsPhrasing(node, context);
816
- switch (normalizeComponentName(node.name)) {
817
- case "strong":
818
- case "b":
819
- return [{ type: "strong", children }];
820
- case "em":
821
- case "i":
822
- return [{ type: "emphasis", children }];
823
- case "code":
824
- return [{ type: "inlineCode", value: extractText(children) }];
825
- case "br":
826
- return [{ type: "break" }];
827
- default:
828
- return children;
829
- }
830
- }
831
- function blockquoteHtmlHandler(node, context) {
832
- return handlerDiagnostic(node, context), [
833
- {
834
- type: "blockquote",
835
- children: node.type === "mdxJsxTextElement" ? paragraphBlock(context.transformChildrenAsPhrasing(node, context)) : context.transformChildrenAsBlocks(node, context)
836
- }
837
- ];
838
- }
839
- function paragraphHtmlHandler(node, context) {
840
- return handlerDiagnostic(node, context), paragraphBlock(context.transformChildrenAsPhrasing(node, context));
841
- }
842
- function preformattedText(node, context) {
843
- if (node.type === "mdxTextExpression" || node.type === "mdxFlowExpression") {
844
- let statement = node.data?.estree?.body?.[0], expression = statement?.type === "ExpressionStatement" ? statement.expression : void 0;
845
- return expression?.type === "Literal" && typeof expression.value == "string" ? expression.value : expression?.type === "TemplateLiteral" && expression.expressions?.length === 0 ? expression.quasis?.[0]?.value.cooked ?? "" : (addDiagnostic(context, {
846
- code: "mdx-expression-dropped",
847
- message: "Dropped non-literal MDX expression from preformatted Markdown output."
848
- }), "");
849
- }
850
- return typeof node.value == "string" ? node.value : (node.children ?? []).map((child, index) => (index > 0 && child.type === "paragraph" ? `
851
-
852
- ` : "") + preformattedText(child, context)).join("");
853
- }
854
- function preHtmlHandler(node, context) {
855
- handlerDiagnostic(node, context);
856
- let code = node.children?.find((child) => child.name === "code");
857
- return [
858
- { type: "code", lang: code ? firstAttribute(code, ["className", "class"], context)?.match(
859
- /(?:^|\s)language-([^\s]+)/
860
- )?.[1] : void 0, value: preformattedText(node, context) }
861
- ];
862
- }
863
-
864
- // src/lib/compiler/markdown/handlers/inline.ts
865
- function iconHandler(node, context) {
866
- handlerDiagnostic(node, context);
867
- let label = firstAttribute(
868
- node,
869
- ["aria-label", "label", "title", "alt"],
870
- context
871
- );
872
- return label ? [text(label)] : [];
873
- }
874
- function linkHandler(node, context) {
875
- let href = firstAttribute(node, ["href", "to", "url"], context);
876
- if (!href) return context.transformChildrenAsPhrasing(node, context);
877
- handlerDiagnostic(node, context);
878
- let children = context.transformChildrenAsPhrasing(node, context);
879
- return [{
880
- type: "link",
881
- url: context.resolveMediaUrl(href),
882
- children: children.length > 0 ? children : [text(href)]
883
- }];
884
- }
885
- function kbdHandler(node, context) {
886
- handlerDiagnostic(node, context);
887
- let label = extractText(context.transformChildrenAsPhrasing(node, context)) || firstAttribute(node, ["children", "label"], context);
888
- return label ? [inlineCode(label)] : [];
889
- }
890
- function inlineComponentHandlers() {
891
- return [
892
- {
893
- componentNames: ["icon"],
894
- transform: iconHandler
895
- },
896
- {
897
- componentNames: ["a", "link"],
898
- transform: linkHandler
899
- },
900
- {
901
- componentNames: ["kbd"],
902
- transform: kbdHandler
903
- }
904
- ];
905
- }
906
-
907
- // src/lib/compiler/markdown/handlers/media.ts
908
- function imageHandler(node, context, kind) {
909
- let src = firstAttribute(node, ["src", "href"], context);
910
- if (!src) return [];
911
- handlerDiagnostic(node, context);
912
- let caption = firstAttribute(node, ["caption"], context), image = {
913
- type: "image",
914
- url: context.resolveMediaUrl(src),
915
- alt: getAttribute(node, "alt", context) ?? firstAttribute(node, ["title", "aria-label"], context) ?? "",
916
- title: firstAttribute(node, ["title"], context)
917
- };
918
- return kind === "inline" ? [image, ...caption ? [text(` ${caption}`)] : []] : [
919
- ...paragraphBlock([image]),
920
- ...caption ? paragraphBlock([text(caption)]) : []
921
- ];
922
- }
923
- function figureCaptionHandler(node, context) {
924
- return handlerDiagnostic(node, context), node.type === "mdxJsxTextElement" ? paragraphBlock(context.transformChildrenAsPhrasing(node, context)) : context.transformChildrenAsBlocks(node, context);
925
- }
926
- function isCaption(node) {
927
- return ["figcaption", "figurecaption", "figure.caption"].includes(
928
- normalizeComponentName(node.name)
929
- );
930
- }
931
- function hasDirectCaption(node) {
932
- return (node.children ?? []).some(
933
- (child) => isCaption(child) || (child.type === "paragraph" || child.type.startsWith("mdxJsx") && child.name === null) && hasDirectCaption(child)
934
- );
935
- }
936
- function figureHandler(node, context) {
937
- handlerDiagnostic(node, context);
938
- let children = context.transformChildrenAsBlocks(node, context);
939
- if (hasDirectCaption(node)) return children;
940
- let caption = firstAttribute(node, ["caption"], context);
941
- return [...children, ...caption ? paragraphBlock([text(caption)]) : []];
942
- }
943
- function videoHandler(node, context, kind) {
944
- let src = firstAttribute(node, ["src", "url", "href"], context);
945
- if (!src) return context.transformChildrenAsBlocks(node, context);
946
- handlerDiagnostic(node, context);
947
- let label = firstAttribute(node, ["title", "alt", "aria-label"], context) ?? "Video", link = {
948
- type: "link",
949
- url: context.resolveMediaUrl(src),
950
- children: [text(label)]
951
- }, children = [text("Video: "), link], caption = firstAttribute(node, ["caption"], context);
952
- return kind === "inline" ? [...children, ...caption ? [text(` ${caption}`)] : []] : [
953
- ...paragraphBlock(children),
954
- ...caption ? paragraphBlock([text(caption)]) : []
955
- ];
956
- }
957
-
958
- // src/lib/compiler/markdown/handlers/passthrough.ts
959
- function passthroughBlockHandler(node, context) {
960
- return handlerDiagnostic(node, context), context.transformChildrenAsBlocks(node, context);
961
- }
962
- function detailsHandler(node, context) {
963
- handlerDiagnostic(node, context);
964
- let summary = firstAttribute(node, ["summary", "title", "label"], context) ?? "Details";
965
- return [
966
- { type: "paragraph", children: [strongLabel(summary)] },
967
- ...context.transformChildrenAsBlocks(node, context)
968
- ];
969
- }
970
-
971
- // src/lib/compiler/markdown/handlers/registry.ts
972
- var defaultHandlers = [
973
- {
974
- componentNames: ["image", "img", "picture"],
975
- transform: imageHandler
976
- },
977
- ...inlineComponentHandlers(),
978
- {
979
- componentNames: ["callout", "admonition", "note", "warning"],
980
- transform: calloutHandler
981
- },
982
- {
983
- componentNames: ["codeblock"],
984
- transform: codeBlockHandler
985
- },
986
- {
987
- componentNames: ["strong", "b", "em", "i", "code", "span", "br"],
988
- transform: inlineHtmlHandler
989
- },
990
- {
991
- componentNames: ["pre"],
992
- transform: preHtmlHandler
993
- },
994
- {
995
- componentNames: ["blockquote"],
996
- transform: blockquoteHtmlHandler
997
- },
998
- {
999
- componentNames: ["p"],
1000
- transform: paragraphHtmlHandler
1001
- },
1002
- {
1003
- componentNames: ["div"],
1004
- transform: passthroughBlockHandler
1005
- },
1006
- {
1007
- componentNames: [
1008
- "codetab",
1009
- "codetabs",
1010
- "tab",
1011
- "tabpanel",
1012
- "tabs",
1013
- "tabscontent"
1014
- ],
1015
- transform: tabHandler
1016
- },
1017
- {
1018
- componentNames: ["figure"],
1019
- transform: figureHandler
1020
- },
1021
- {
1022
- componentNames: ["figcaption", "figurecaption", "figure.caption"],
1023
- transform: figureCaptionHandler
1024
- },
1025
- {
1026
- componentNames: ["video", "videoiframe", "iframe"],
1027
- transform: videoHandler
1028
- },
1029
- {
1030
- componentNames: ["details", "accordion", "summary"],
1031
- transform: detailsHandler
1032
- },
1033
- {
1034
- componentNames: ["step", "steps"],
1035
- transform: passthroughBlockHandler
1036
- }
1037
- ], handlersByName = new Map(
1038
- defaultHandlers.flatMap(
1039
- (handler) => handler.componentNames.map((name) => [name, handler])
1040
- )
1041
- );
1042
-
1043
- // src/lib/compiler/markdown/convert-mdx-to-markdown.ts
1044
- var processor = unified().use(remarkParse).use(remarkMdx).use(remarkGfm).use(remarkStringify, {
1045
- bullet: "-",
1046
- fences: !0,
1047
- incrementListMarker: !0,
1048
- listItemIndent: "one",
1049
- rule: "-"
1050
- });
1051
- function stripHtmlComments(source) {
1052
- return source.replace(/<!--[\s\S]*?-->/g, "");
1053
- }
1054
- function stripTopLevelImports(source) {
1055
- return source.split(`
1056
- `).filter((line) => !/^\s*(?:import|export)\s/.test(line)).join(`
1057
- `).trim();
1058
- }
1059
- function parseSource(source, context) {
1060
- try {
1061
- return processor.parse(source);
1062
- } catch (error) {
1063
- let withoutComments = stripHtmlComments(source);
1064
- if (withoutComments !== source)
1065
- try {
1066
- return processor.parse(withoutComments);
1067
- } catch {
1068
- }
1069
- addDiagnostic(context, {
1070
- code: "mdx-parse-failed",
1071
- message: error instanceof Error ? error.message : "Failed to parse MDX for Markdown output."
1072
- });
1073
- return;
1074
- }
1075
- }
1076
- function transformChildrenAsBlocks(node, context) {
1077
- return (node.children ?? []).flatMap(
1078
- (child) => transformBlockNode(child, context)
1079
- );
1080
- }
1081
- function transformChildrenAsPhrasing(node, context) {
1082
- return (node.children ?? []).flatMap(
1083
- (child) => transformInlineNode(child, context)
1084
- );
1085
- }
1086
- function transformMdxElement(node, context, kind) {
1087
- if (node.name === null)
1088
- return kind === "block" ? transformChildrenAsBlocks(node, context) : transformChildrenAsPhrasing(node, context);
1089
- if (node.name && context.importedIcons.has(node.name)) {
1090
- let label = firstAttribute(node, ["aria-hidden"], context) === "true" ? "" : firstAttribute(node, ["aria-label", "title"], context), children2 = label ? [{ type: "text", value: label }] : [];
1091
- return kind === "block" ? paragraphBlock(children2) : children2;
1092
- }
1093
- let componentName = normalizeComponentName(node.name), handler = handlersByName.get(componentName);
1094
- if (handler) {
1095
- let result = handler.transform(node, context, kind);
1096
- return kind === "block" ? asBlocks(result) : asPhrasing(result);
1097
- }
1098
- if (addDiagnostic(context, {
1099
- code: "mdx-component-unknown",
1100
- componentName: node.name ?? void 0,
1101
- message: `Unknown MDX component "${node.name ?? "unknown"}".`
1102
- }), context.unknownComponentMode === "error")
1103
- throw new Error(`Unknown MDX component "${node.name ?? "unknown"}".`);
1104
- if (context.unknownComponentMode === "html") {
1105
- let html = htmlFallback(node);
1106
- return html ? [{ type: "html", value: html }] : [];
1107
- }
1108
- let children = kind === "inline" ? transformChildrenAsPhrasing(node, context) : transformChildrenAsBlocks(node, context);
1109
- return kind === "block" ? asBlocks(children) : asPhrasing(children);
1110
- }
1111
- function htmlFallback(node) {
1112
- if (!node.name) return;
1113
- let attrs = (node.attributes ?? []).map((attr) => {
1114
- if (attr.value === null || attr.value === void 0) return attr.name;
1115
- if (typeof attr.value == "string")
1116
- return `${attr.name}="${attr.value.replace(/"/g, "&quot;")}"`;
1117
- }).filter((value) => !!value).join(" ");
1118
- return `<${node.name}${attrs ? ` ${attrs}` : ""} />`;
1119
- }
1120
- function transformInlineNode(node, context) {
1121
- if (node.type === "mdxJsxTextElement")
1122
- return asPhrasing(transformMdxElement(node, context, "inline"));
1123
- if (node.type === "mdxTextExpression")
1124
- return addDiagnostic(context, {
1125
- code: "mdx-expression-dropped",
1126
- message: "Dropped inline MDX expression from Markdown output."
1127
- }), [];
1128
- switch (node.type) {
1129
- case "text":
1130
- case "inlineCode":
1131
- case "break":
1132
- case "footnoteReference":
1133
- return [node];
1134
- case "emphasis":
1135
- return [{
1136
- ...node,
1137
- children: transformChildrenAsPhrasing(node, context)
1138
- }];
1139
- case "strong":
1140
- return [{
1141
- ...node,
1142
- children: transformChildrenAsPhrasing(node, context)
1143
- }];
1144
- case "delete":
1145
- return [{
1146
- ...node,
1147
- children: transformChildrenAsPhrasing(node, context)
1148
- }];
1149
- case "link":
1150
- return [{
1151
- ...node,
1152
- url: context.resolveMediaUrl(node.url),
1153
- children: transformChildrenAsPhrasing(node, context)
1154
- }];
1155
- case "image":
1156
- return [
1157
- {
1158
- ...node,
1159
- url: context.resolveMediaUrl(node.url)
1160
- }
1161
- ];
1162
- case "linkReference":
1163
- case "imageReference":
1164
- return [node];
1165
- case "html":
1166
- return [node];
1167
- default:
1168
- return [];
1169
- }
1170
- }
1171
- function transformListItem(node, context) {
1172
- return {
1173
- ...node,
1174
- children: transformChildrenAsBlocks(node, context)
1175
- };
1176
- }
1177
- function transformTableCell(node, context) {
1178
- return {
1179
- ...node,
1180
- children: transformChildrenAsPhrasing(node, context)
1181
- };
1182
- }
1183
- function transformTableRow(node, context) {
1184
- return {
1185
- ...node,
1186
- children: (node.children ?? []).map(
1187
- (child) => transformTableCell(child, context)
1188
- )
1189
- };
1190
- }
1191
- function isFigureBlock(node) {
1192
- if (node.type !== "mdxJsxTextElement" && node.type !== "mdxJsxFlowElement")
1193
- return !1;
1194
- if (node.name === null) return (node.children ?? []).some(isFigureBlock);
1195
- let name = normalizeComponentName(node.name);
1196
- return [
1197
- "figure",
1198
- "figcaption",
1199
- "figurecaption",
1200
- "figure.caption",
1201
- "pre",
1202
- "blockquote"
1203
- ].includes(name) || ["image", "img", "video", "videoiframe", "iframe"].includes(name) && (node.attributes ?? []).some((attribute) => attribute.name === "caption");
1204
- }
1205
- function transformBlockNode(node, context) {
1206
- if (node.type === "mdxjsEsm")
1207
- return addDiagnostic(context, {
1208
- code: "mdx-esm-dropped",
1209
- message: "Dropped MDX import/export from Markdown output."
1210
- }), [];
1211
- if (node.type === "mdxFlowExpression")
1212
- return addDiagnostic(context, {
1213
- code: "mdx-expression-dropped",
1214
- message: "Dropped flow MDX expression from Markdown output."
1215
- }), [];
1216
- if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement")
1217
- return transformMdxElement(node, context, "block");
1218
- switch (node.type) {
1219
- case "paragraph": {
1220
- if ((node.children ?? []).some(isFigureBlock)) {
1221
- let blocks = [], pending = [], flushPhrasing = () => {
1222
- let children = transformChildrenAsPhrasing(
1223
- { type: "paragraph", children: pending },
1224
- context
1225
- );
1226
- children.some(
1227
- (child) => child.type !== "text" || child.value.trim()
1228
- ) && blocks.push(...paragraphBlock(children)), pending = [];
1229
- };
1230
- for (let child of node.children ?? [])
1231
- isFigureBlock(child) ? (flushPhrasing(), blocks.push(...transformBlockNode(child, context))) : pending.push(child);
1232
- return flushPhrasing(), blocks;
1233
- }
1234
- let paragraphNode = paragraph(
1235
- transformChildrenAsPhrasing(node, context)
1236
- );
1237
- return paragraphNode ? [paragraphNode] : [];
1238
- }
1239
- case "heading":
1240
- return [{
1241
- ...node,
1242
- children: transformChildrenAsPhrasing(node, context)
1243
- }];
1244
- case "blockquote":
1245
- return [
1246
- {
1247
- ...node,
1248
- children: transformChildrenAsBlocks(node, context)
1249
- }
1250
- ];
1251
- case "list":
1252
- return [{
1253
- ...node,
1254
- children: (node.children ?? []).map(
1255
- (child) => transformListItem(child, context)
1256
- )
1257
- }];
1258
- case "table":
1259
- return [{
1260
- ...node,
1261
- children: (node.children ?? []).map(
1262
- (child) => transformTableRow(child, context)
1263
- )
1264
- }];
1265
- case "text":
1266
- case "inlineCode":
1267
- case "break":
1268
- case "emphasis":
1269
- case "strong":
1270
- case "delete":
1271
- case "link":
1272
- case "image":
1273
- case "footnoteReference":
1274
- return paragraphBlock(transformInlineNode(node, context));
1275
- case "code":
1276
- case "thematicBreak":
1277
- case "html":
1278
- case "yaml":
1279
- case "footnoteDefinition":
1280
- return [node];
1281
- case "definition":
1282
- return [
1283
- {
1284
- ...node,
1285
- url: context.resolveMediaUrl(node.url)
1286
- }
1287
- ];
1288
- default:
1289
- return [];
1290
- }
1291
- }
1292
- function convertMdxToMarkdown(options) {
1293
- let context = {
1294
- importedIcons: /* @__PURE__ */ new Set(),
1295
- collectDiagnostics: options.collectDiagnostics ?? !0,
1296
- diagnostics: [],
1297
- resolveMediaUrl: (url) => String(mapSourceAssetUrl(url)),
1298
- transformChildrenAsBlocks,
1299
- transformChildrenAsPhrasing,
1300
- unknownComponentMode: options.unknownComponentMode ?? "children"
1301
- }, parsed = parseSource(options.source, context);
1302
- if (!parsed)
1303
- return {
1304
- diagnostics: context.diagnostics,
1305
- markdown: stripTopLevelImports(options.source),
1306
- text: ""
1307
- };
1308
- for (let node of parsed.children)
1309
- if (node.type === "mdxjsEsm") {
1310
- for (let statement of node.data?.estree?.body ?? [])
1311
- if (!(statement.type !== "ImportDeclaration" || statement.source.value !== mdxIconPackage))
1312
- for (let specifier of statement.specifiers)
1313
- specifier.type === "ImportSpecifier" && context.importedIcons.add(specifier.local.name);
1314
- }
1315
- let transformed = {
1316
- type: "root",
1317
- children: parsed.children.flatMap(
1318
- (child) => transformBlockNode(child, context)
1319
- )
1320
- }, markdown = String(processor.stringify(transformed)).trim();
1321
- return {
1322
- diagnostics: context.diagnostics,
1323
- markdown,
1324
- text: extractText(transformed.children)
1325
- };
1326
- }
1327
-
1328
615
  // src/lib/compiler/markdown/page-artifact-conversions.ts
1329
- function convertPublishedPageBodies(manifest) {
616
+ function convertPageBodies(manifest, includeDrafts = !1) {
1330
617
  return new Map(
1331
- manifest.pages.filter((page) => page.status === "published").map((page) => [
618
+ manifest.pages.filter(
619
+ (page) => includeDrafts || page.frontmatter.status === "published"
620
+ ).map((page) => [
1332
621
  page.path,
1333
622
  convertMdxToMarkdown({
1334
623
  collectDiagnostics: !1,
@@ -1605,7 +894,8 @@ ${entries.join(`,
1605
894
  // src/lib/runtime/metadata/canonical-url.ts
1606
895
  function canonicalUrl(manifest, path, group) {
1607
896
  let origin = (group && manifest.groups[group]?.origin) ?? manifest.site.origin;
1608
- if (!origin) return path;
897
+ if (!origin)
898
+ return /^[a-z][a-z\d+.-]*:/i.test(path) ? path : new URL(path, "https://publish.invalid").pathname;
1609
899
  try {
1610
900
  return new URL(path, `${origin}/`).toString();
1611
901
  } catch {
@@ -1614,7 +904,7 @@ function canonicalUrl(manifest, path, group) {
1614
904
  }
1615
905
  function navigationUrl(manifest, path, group) {
1616
906
  let origin = manifest.groups[group]?.origin ?? manifest.site.origin;
1617
- return !origin || origin === manifest.site.origin ? path : canonicalUrl(manifest, path, group);
907
+ return !origin || origin === manifest.site.origin ? new URL(path, "https://publish.invalid").pathname : canonicalUrl(manifest, path, group);
1618
908
  }
1619
909
 
1620
910
  // src/route-not-found.ts
@@ -1672,10 +962,8 @@ function serializeNotFoundMarkdown({
1672
962
 
1673
963
  // src/lib/compiler/nextjs/not-found-plan.ts
1674
964
  var belongsToPath = (path, base) => base === "/" || path === base || path.startsWith(`${base}/`), outputDirectory = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
1675
- function orderedPagePaths(tree) {
1676
- return tree.flatMap(
1677
- (node) => node.type === "page" ? [node.path] : "children" in node ? orderedPagePaths(node.children) : []
1678
- );
965
+ function orderedPagePaths(navigation) {
966
+ return navigation.flatMap((node) => node.type === "page" ? [node.page.path] : "children" in node && node.children ? orderedPagePaths(node.children) : node.type === "view" ? orderedPagePaths(node.items) : []);
1679
967
  }
1680
968
  function createNotFoundScopes({
1681
969
  manifest,
@@ -1697,10 +985,10 @@ function createNotFoundScopes({
1697
985
  href: url(group.path)
1698
986
  });
1699
987
  let visible = manifest.pages.filter(
1700
- (page) => page.status === "published" && page.meta.hidden !== !0 && (manifest.groups[page.group]?.origin ?? manifest.site.origin) === origin && belongsToPath(page.path, path) && (!group || page.group === group.key || manifest.groups[page.group]?.ancestry?.includes(group.key))
1701
- ), byPath = new Map(visible.map((page) => [page.path, page])), first = orderedPagePaths(group?.tree ?? manifest.tree).map((entry) => byPath.get(entry)).find(Boolean) ?? visible[0];
988
+ (page) => page.frontmatter.status === "published" && (manifest.groups[page.group]?.origin ?? manifest.site.origin) === origin && belongsToPath(page.path, path) && (!group || page.group === group.key || manifest.groups[page.group]?.ancestry?.includes(group.key))
989
+ ), byPath = new Map(visible.map((page) => [page.path, page])), first = (group ? orderedPagePaths(group.navigation.items) : []).map((entry) => byPath.get(entry)).find(Boolean) ?? visible[0];
1702
990
  first && first.path !== path && recommendations.push({
1703
- title: first.title,
991
+ title: first.frontmatter.title,
1704
992
  href: url(first.path, first.group)
1705
993
  }), rootOwned && !recommendations.some((link) => link.href === url("/")) && recommendations.push({ title: "Go home", href: url("/") });
1706
994
  let available = outputs.filter((output) => {
@@ -1841,7 +1129,9 @@ async function resolveNearestPrimitiveOwner(config, groupKey, file) {
1841
1129
  details: { group: groupKey, file }
1842
1130
  });
1843
1131
  for (let index = group.ancestry.length - 1; index >= 0; index -= 1) {
1844
- let ancestor = config.groups[group.ancestry[index]];
1132
+ let ancestorKey = group.ancestry[index];
1133
+ if (!ancestorKey) continue;
1134
+ let ancestor = config.groups[ancestorKey];
1845
1135
  if (ancestor && await hasSourcePrimitive(ancestor, file))
1846
1136
  return ancestor.key;
1847
1137
  }
@@ -1939,8 +1229,8 @@ import sharp from "sharp";
1939
1229
 
1940
1230
  // src/lib/runtime/metadata/og-text.ts
1941
1231
  var segmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
1942
- function ogTextGraphemes(text2) {
1943
- return [...segmenter.segment(text2)].map(({ segment }) => segment);
1232
+ function ogTextGraphemes(text) {
1233
+ return [...segmenter.segment(text)].map(({ segment }) => segment);
1944
1234
  }
1945
1235
  function truncateGraphemes(value, maximum) {
1946
1236
  let result = "";
@@ -2404,11 +1694,11 @@ function inheritedOgImage({
2404
1694
  }) {
2405
1695
  let ancestry = manifest.groups[groupKey]?.ancestry ?? [];
2406
1696
  for (let key of [...ancestry].reverse()) {
2407
- let og = manifest.groups[key]?.og;
1697
+ let og = manifest.groups[key]?.metadata?.openGraph;
2408
1698
  if (og?.scope === "subtree" && og.image !== void 0)
2409
1699
  return og.image;
2410
1700
  }
2411
- return manifest.site.og?.scope === "subtree" ? manifest.site.og.image : void 0;
1701
+ return manifest.site.metadata?.openGraph?.scope === "subtree" ? manifest.site.metadata?.openGraph?.image : void 0;
2412
1702
  }
2413
1703
  function displayGroup(group) {
2414
1704
  return group && group !== ROOT_CONTENT_GROUP_KEY ? group : void 0;
@@ -2616,9 +1906,9 @@ async function buildOgManifest(options) {
2616
1906
  manifest: ogManifest,
2617
1907
  route: {
2618
1908
  routePath: "/",
2619
- title: site.og?.title ?? site.name,
2620
- description: site.og?.description ?? site.description,
2621
- og: site.og
1909
+ title: site.metadata?.openGraph?.title ?? site.name,
1910
+ description: site.metadata?.openGraph?.description ?? site.description,
1911
+ og: site.metadata?.openGraph
2622
1912
  },
2623
1913
  mediaManifest: options.mediaManifest,
2624
1914
  generatedDefaults
@@ -2632,7 +1922,7 @@ async function buildOgManifest(options) {
2632
1922
  description: site.description,
2633
1923
  group: group.key,
2634
1924
  label: groupLabel(group.key),
2635
- og: group.og
1925
+ og: group.metadata?.openGraph
2636
1926
  },
2637
1927
  fallbackImage: inheritedOgImage({
2638
1928
  groupKey: group.key,
@@ -2646,11 +1936,11 @@ async function buildOgManifest(options) {
2646
1936
  manifest: ogManifest,
2647
1937
  route: {
2648
1938
  routePath: page.path,
2649
- title: page.title,
2650
- description: page.description ?? site.description,
1939
+ title: page.frontmatter.title,
1940
+ description: page.frontmatter.description || site.description,
2651
1941
  group: page.group,
2652
1942
  label: groupLabel(page.group),
2653
- og: page.og
1943
+ og: page.frontmatter.metadata.openGraph
2654
1944
  },
2655
1945
  fallbackImage: inheritedOgImage({
2656
1946
  groupKey: page.group,
@@ -2673,13 +1963,13 @@ async function prepareOgTextAssets({
2673
1963
  "next/dist/compiled/@vercel/og/Geist-Regular.ttf"
2674
1964
  )
2675
1965
  );
2676
- let platform = await platformCoverage, text2 = getOgImageText(entry), displayed = {
1966
+ let platform = await platformCoverage, text = getOgImageText(entry), displayed = {
2677
1967
  main: [
2678
- ...text2.titleLines,
2679
- ...text2.descriptionLines,
2680
- entry.logo?.dataUri ? "" : text2.siteName ?? ""
1968
+ ...text.titleLines,
1969
+ ...text.descriptionLines,
1970
+ entry.logo?.dataUri ? "" : text.siteName ?? ""
2681
1971
  ],
2682
- mono: [text2.label ?? "", text2.displayUrl.host, text2.displayUrl.path]
1972
+ mono: [text.label ?? "", text.displayUrl.host, text.displayUrl.path]
2683
1973
  }, covered = (font, segment) => "hasGlyphForCodePoint" in font && [...segment].every(
2684
1974
  (point) => font.hasGlyphForCodePoint(point.codePointAt(0))
2685
1975
  ), candidates = (role) => [...new Set(displayed[role].flatMap(ogTextGraphemes))].filter(
@@ -2938,7 +2228,7 @@ async function prepareAppInstallation({
2938
2228
  readGraphFile("pnpm-workspace.yaml"),
2939
2229
  readGraphFile("pnpm-lock.yaml.template")
2940
2230
  ]), graphRecord = installed.release.graphs[graph];
2941
- for (let [name, bytes, expectedHash] of [
2231
+ for (let [name, bytes2, expectedHash] of [
2942
2232
  ["package.json", packageJson, graphRecord.packageJsonSha256],
2943
2233
  ["pnpm-workspace.yaml", pnpmWorkspace, graphRecord.pnpmWorkspaceSha256],
2944
2234
  [
@@ -2947,7 +2237,7 @@ async function prepareAppInstallation({
2947
2237
  graphRecord.lockfileTemplateSha256
2948
2238
  ]
2949
2239
  ])
2950
- if (createHash3("sha256").update(bytes).digest("hex") !== expectedHash)
2240
+ if (createHash3("sha256").update(bytes2).digest("hex") !== expectedHash)
2951
2241
  throw new AppError({
2952
2242
  code: "FRAMEWORK_RELEASE_INVALID",
2953
2243
  message: `The framework ${graph} ${name} hash does not match its release.`
@@ -3063,20 +2353,10 @@ async function materializeAppInstallation({
3063
2353
  import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
3064
2354
  import { dirname as dirname4, resolve as resolve3 } from "path";
3065
2355
  var SEARCH_BUNDLE_DIR = PUBLIC_SEARCH_DIRECTORY, SEARCH_INDEX_SCRIPT_PATH = "scripts/build-search-index.mjs", SEARCH_RECORDS_PATH = ".meta/search-records.json", SEARCH_STATUS_FILE = "publishos-search-status.json", SEARCH_INDEX_STATUS_MESSAGE = "Building search index...";
3066
- function stringifyMetaValue(value) {
3067
- if (typeof value == "string" && value.trim()) return value;
3068
- if (typeof value == "number" || typeof value == "boolean")
3069
- return String(value);
3070
- }
3071
- function pageAuthors(page) {
3072
- return page.authors.flatMap((author) => typeof author.name == "string" && author.name.trim() ? [author.name] : []);
3073
- }
3074
2356
  function pageContent(page, conversions) {
3075
2357
  return [
3076
- page.title,
3077
- page.description,
3078
- page.tags.join(" "),
3079
- pageAuthors(page).join(" "),
2358
+ page.frontmatter.title,
2359
+ page.frontmatter.description,
3080
2360
  conversions.get(page.path)?.text ?? ""
3081
2361
  ].filter(Boolean).join(`
3082
2362
 
@@ -3088,37 +2368,28 @@ function pageMeta({
3088
2368
  }) {
3089
2369
  return Object.fromEntries(
3090
2370
  Object.entries({
3091
- title: page.title,
3092
- description: page.description,
2371
+ title: page.frontmatter.title,
2372
+ description: page.frontmatter.description,
3093
2373
  group: page.group,
3094
2374
  section,
3095
2375
  path: page.path,
3096
- nodeId: page.fileID,
3097
- tags: page.tags.join(","),
3098
- author: pageAuthors(page).join(","),
3099
- publishedAt: page.publishedAt,
3100
- updatedAt: page.updatedAt,
3101
- ...page.meta
3102
- }).map(([key, value]) => [key, stringifyMetaValue(value)]).filter((entry) => !!entry[1])
2376
+ nodeId: page.fileID
2377
+ }).filter((entry) => !!entry[1])
3103
2378
  );
3104
2379
  }
3105
2380
  function pagesToIndex(manifest) {
3106
- return manifest.pages.filter((page) => page.status === "published").sort((a, b) => a.path.localeCompare(b.path));
2381
+ return manifest.pages.filter((page) => page.frontmatter.status === "published").sort((a, b) => a.path.localeCompare(b.path));
3107
2382
  }
3108
2383
  function createSearchRecords(manifest, conversions) {
3109
2384
  return pagesToIndex(manifest).map((page) => ({
3110
2385
  content: pageContent(page, conversions),
3111
2386
  filters: {
3112
- author: pageAuthors(page),
3113
- group: [page.group],
3114
- tag: page.tags
2387
+ group: [page.group]
3115
2388
  },
3116
2389
  language: "en",
3117
2390
  meta: pageMeta({ page, section: manifest.groups[page.group]?.title }),
3118
2391
  sort: {
3119
- publishedAt: page.publishedAt ?? "",
3120
- title: page.title,
3121
- updatedAt: page.updatedAt ?? ""
2392
+ title: page.frontmatter.title
3122
2393
  },
3123
2394
  url: canonicalUrl(manifest, page.path, page.group)
3124
2395
  }));
@@ -4093,54 +3364,73 @@ ${derivedThemeCss(defaultsTemplate, selectors.join(`,
4093
3364
  function discoveryOrigin(manifest, group) {
4094
3365
  return (group ? manifest.groups[group]?.origin : void 0) ?? manifest.site.origin;
4095
3366
  }
4096
- function pathWithin(path, basePath) {
4097
- return basePath === "/" || path === basePath || path.startsWith(`${basePath}/`);
4098
- }
4099
- function discoveryPages({
3367
+ function navigationDiscoverySections({
4100
3368
  manifest,
4101
3369
  group,
4102
3370
  scope,
4103
- limit
4104
- }) {
4105
- let origin = discoveryOrigin(manifest, group), basePath = group ? manifest.groups[group]?.path ?? "/" : "/", pages = manifest.pages.filter((page) => page.status !== "published" || discoveryOrigin(manifest, page.group) !== origin ? !1 : !group || scope === "site" ? !0 : pathWithin(page.path, basePath) ? page.group === group || scope !== "self" && page.group.startsWith(`${group}/`) : !1);
4106
- return limit ? pages.slice(0, limit) : pages;
4107
- }
4108
- function groupDiscoveryPages({
4109
- manifest,
4110
- pages
3371
+ limit,
3372
+ excludeGroups = [],
3373
+ markdownPaths
4111
3374
  }) {
4112
- let sections = /* @__PURE__ */ new Map();
4113
- for (let page of pages) {
4114
- let group = manifest.groups[page.group], key = group ? page.group : "", section = sections.get(key);
4115
- section || (section = {
4116
- ...group ? { group: page.group } : {},
4117
- title: group ? group.key === "__root" ? manifest.site.name : group.title : "Pages",
4118
- pages: []
4119
- }, sections.set(key, section)), section.pages.push(page);
4120
- }
4121
- return [...sections.values()].sort(
4122
- (a, b) => (manifest.groups[a.group ?? ""]?.path ?? "/").localeCompare(
4123
- manifest.groups[b.group ?? ""]?.path ?? "/"
4124
- ) || a.title.localeCompare(b.title)
4125
- );
3375
+ let origin = discoveryOrigin(manifest, group), targetGroup = group ? manifest.groups[group] : void 0, mountedGroup = Object.values(manifest.groups).find(
3376
+ (candidate) => candidate.key !== "__root" && candidate.path === "/" && discoveryOrigin(manifest, candidate.key) === origin
3377
+ ), rootGroups = targetGroup && scope !== "site" ? [manifest.groups[targetGroup.navigation.rootGroup]] : Object.values(manifest.groups).filter(
3378
+ (candidate) => candidate.key === candidate.navigation.rootGroup && (candidate.key !== "__root" || !mountedGroup) && discoveryOrigin(manifest, candidate.key) === origin
3379
+ ), sections = [], pageKeys = /* @__PURE__ */ new Set(), pageCount = 0, inScope = (entryGroup) => discoveryOrigin(manifest, entryGroup) !== origin || excludeGroups.some(
3380
+ (excluded) => entryGroup === excluded || entryGroup.startsWith(`${excluded}/`)
3381
+ ) ? !1 : !targetGroup || scope === "site" ? !0 : entryGroup === targetGroup.key || scope !== "self" && entryGroup.startsWith(`${targetGroup.key}/`), append = (title, entry) => {
3382
+ let previous = sections.at(-1);
3383
+ previous?.title === title ? previous.entries.push(entry) : sections.push({ title, entries: [entry] });
3384
+ }, visit = (nodes, groupKey, headings) => {
3385
+ for (let node of nodes)
3386
+ if (node.type === "page") {
3387
+ let page = node.page, key = `${page.group}/${page.fileID}`;
3388
+ if (page.frontmatter.status !== "published" || !inScope(page.group) || pageKeys.has(key) || limit !== void 0 && limit > 0 && pageCount >= limit)
3389
+ continue;
3390
+ pageKeys.add(key), pageCount += 1, append(headings.join(" > "), {
3391
+ type: "page",
3392
+ title: node.label || page.frontmatter.title,
3393
+ url: canonicalUrl(
3394
+ manifest,
3395
+ markdownPaths?.get(page.path) ?? page.path,
3396
+ page.group
3397
+ ),
3398
+ path: page.path,
3399
+ group: page.group,
3400
+ ...page.frontmatter.description ? { description: page.frontmatter.description } : {}
3401
+ }), node.children && visit(node.children, page.group, headings);
3402
+ } else node.type === "group" ? (inScope(node.groupKey) && append(headings.join(" > "), {
3403
+ type: "link",
3404
+ title: node.label,
3405
+ url: canonicalUrl(manifest, node.route, node.groupKey),
3406
+ group: node.groupKey
3407
+ }), visit(node.children, node.groupKey, [...headings, node.label])) : node.type === "link" ? inScope(groupKey) && append(headings.join(" > "), {
3408
+ type: "link",
3409
+ title: node.label,
3410
+ url: canonicalUrl(manifest, node.href, groupKey),
3411
+ group: groupKey
3412
+ }) : node.type === "section" || node.type === "folder" ? visit(node.children, groupKey, [...headings, node.label]) : node.type === "view" && visit(node.items, groupKey, headings);
3413
+ };
3414
+ for (let root of rootGroups)
3415
+ !root || discoveryOrigin(manifest, root.key) !== origin || (!targetGroup || scope === "site" ? append(root.title, {
3416
+ type: "link",
3417
+ title: root.title,
3418
+ url: canonicalUrl(manifest, root.path, root.key),
3419
+ group: root.key
3420
+ }) : root.key === targetGroup.key && append(root.title, {
3421
+ type: "link",
3422
+ title: root.title,
3423
+ url: canonicalUrl(manifest, root.path, root.key),
3424
+ group: root.key
3425
+ }), visit(root.navigation.items, root.key, [root.title]));
3426
+ return sections;
4126
3427
  }
4127
3428
 
4128
3429
  // src/lib/runtime/metadata/feed-serialization.ts
3430
+ var siteBuildTime = process.env.PUBLISHOS_SITE_BUILD_TIME ?? (/* @__PURE__ */ new Date()).toISOString();
4129
3431
  function xmlEscape(value) {
4130
3432
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
4131
3433
  }
4132
- function latestDate(pages) {
4133
- let timestamp = Math.max(
4134
- 0,
4135
- ...pages.map((page) => {
4136
- let date = page.publishedAt ?? page.updatedAt;
4137
- if (!date) return -1 / 0;
4138
- let parsed = Date.parse(date);
4139
- return Number.isNaN(parsed) ? -1 / 0 : parsed;
4140
- })
4141
- );
4142
- return new Date(timestamp).toISOString();
4143
- }
4144
3434
  function serializeRssFeed(input) {
4145
3435
  let description = input.description ?? input.title, items = input.pages.map((page) => {
4146
3436
  let publishedAt = page.publishedAt, pubDate = publishedAt ? `
@@ -4164,27 +3454,6 @@ ${items}
4164
3454
  </rss>
4165
3455
  `;
4166
3456
  }
4167
- function serializeAtomFeed(input) {
4168
- let entries = input.pages.map((page) => {
4169
- let date = page.publishedAt ?? page.updatedAt, updated = date ? new Date(date).toISOString() : latestDate([]), summary = page.description ? `
4170
- <summary>${xmlEscape(page.description)}</summary>` : "";
4171
- return ` <entry>
4172
- <title>${xmlEscape(page.title)}</title>
4173
- <id>${xmlEscape(page.url)}</id>
4174
- <link href="${xmlEscape(page.url)}" />
4175
- <updated>${xmlEscape(updated)}</updated>${summary}
4176
- </entry>`;
4177
- }).join(`
4178
- `);
4179
- return `<?xml version="1.0" encoding="UTF-8"?>
4180
- <feed xmlns="http://www.w3.org/2005/Atom">
4181
- <title>${xmlEscape(input.title)}</title>
4182
- <id>${xmlEscape(input.homeUrl)}</id>
4183
- <updated>${xmlEscape(latestDate(input.pages))}</updated>
4184
- ${entries}
4185
- </feed>
4186
- `;
4187
- }
4188
3457
  function serializeJsonFeed(input) {
4189
3458
  return `${JSON.stringify(
4190
3459
  {
@@ -4198,8 +3467,8 @@ function serializeJsonFeed(input) {
4198
3467
  title: page.title,
4199
3468
  summary: page.description,
4200
3469
  content_text: page.body,
4201
- date_published: page.publishedAt,
4202
- date_modified: page.updatedAt,
3470
+ date_published: page.publishedAt ? new Date(page.publishedAt).toISOString() : void 0,
3471
+ date_modified: page.updatedAt ? new Date(page.updatedAt).toISOString() : void 0,
4203
3472
  tags: page.tags
4204
3473
  }))
4205
3474
  },
@@ -4209,6 +3478,12 @@ function serializeJsonFeed(input) {
4209
3478
  `;
4210
3479
  }
4211
3480
 
3481
+ // src/lib/runtime/metadata/llms-part-path.ts
3482
+ function llmsPartPath(path, suffix) {
3483
+ let filenameStart = path.lastIndexOf("/") + 1, extension = path.lastIndexOf("."), insertAt = extension > filenameStart ? extension : path.length;
3484
+ return `${path.slice(0, insertAt)}-${suffix}${path.slice(insertAt)}`;
3485
+ }
3486
+
4212
3487
  // src/lib/runtime/metadata/llms-serialization.ts
4213
3488
  function inlineText(value) {
4214
3489
  return value.replace(/[\r\n]+/g, " ").replace(/([\\`*_[\]<>])/g, "\\$1");
@@ -4221,30 +3496,18 @@ function markdownLink2(link) {
4221
3496
  return `[${inlineText(link.title)}](${url})`;
4222
3497
  }
4223
3498
  function serializeLlmsIndex(input) {
4224
- let body = (input.sections ?? [
4225
- { title: "Pages", pages: input.pages ?? [] }
4226
- ]).map((section) => {
4227
- let lines = [
4228
- ...(section.links ?? []).map((link) => `- ${markdownLink2(link)}`),
4229
- ...section.pages.map((page) => {
4230
- let description = input.includeDescriptions && page.description ? `: ${inlineText(page.description)}` : "", alternate = page.alternate ? ` (${markdownLink2(page.alternate)})` : "";
4231
- return `- ${markdownLink2(page)}${alternate}${description}`;
4232
- })
4233
- ];
3499
+ let body = (input.sections ?? []).map((section) => {
3500
+ let lines = section.entries.map((entry) => {
3501
+ let description = input.includeDescriptions && entry.description ? `: ${inlineText(entry.description)}` : "", alternate = entry.alternate ? ` (${markdownLink2(entry.alternate)})` : "";
3502
+ return `- ${markdownLink2(entry)}${alternate}${description}`;
3503
+ });
4234
3504
  return `## ${inlineText(section.title)}
4235
3505
 
4236
3506
  ${lines.join(`
4237
3507
  `)}`;
4238
3508
  }).join(`
4239
3509
 
4240
- `), linkLines = (input.links ?? []).map(
4241
- (link) => `- ${markdownLink2(link)}`
4242
- ), linkSection = linkLines.length ? `
4243
-
4244
- ## Group LLM indexes
4245
-
4246
- ${linkLines.join(`
4247
- `)}` : "", otherGroupLines = (input.otherGroupLinks ?? []).map(
3510
+ `), otherGroupLines = (input.otherGroupLinks ?? []).map(
4248
3511
  (link) => `- ${markdownLink2(link)}`
4249
3512
  ), otherGroupSection = otherGroupLines.length ? `
4250
3513
 
@@ -4256,7 +3519,7 @@ ${otherGroupLines.join(`
4256
3519
 
4257
3520
  > ${inlineText(input.summary)}
4258
3521
 
4259
- ${body}${linkSection}${otherGroupSection}
3522
+ ${body}${otherGroupSection}
4260
3523
  `;
4261
3524
  }
4262
3525
  function serializeLlmsFull(input) {
@@ -4268,30 +3531,216 @@ function serializeLlmsFull(input) {
4268
3531
 
4269
3532
  ${references ? `${references}
4270
3533
 
4271
- ` : ""}`, sections = [], totalBytes = byteLength(header), included = 0, truncated = !1;
4272
- for (let artifact of [...input.artifacts].sort(
4273
- (a, b) => a.markdownPath.localeCompare(b.markdownPath)
4274
- )) {
3534
+ ` : ""}`, headerBytes = byteLength(header);
3535
+ if (headerBytes > input.maxBytes)
3536
+ throw new RangeError("LLMS full-content header exceeds the byte limit.");
3537
+ let parts = [], oversized = [], current = header, currentBytes = headerBytes;
3538
+ for (let artifact of input.artifacts) {
4275
3539
  let section = `---
4276
3540
 
4277
3541
  ${artifact.body.trim()}
4278
3542
 
4279
3543
  `, sectionBytes = byteLength(section);
4280
- if (totalBytes + sectionBytes > input.maxBytes) {
4281
- truncated = !0;
4282
- break;
3544
+ if (headerBytes + sectionBytes > input.maxBytes) {
3545
+ oversized.push({
3546
+ markdownPath: artifact.markdownPath,
3547
+ url: artifact.pageUrl ?? artifact.markdownPath
3548
+ });
3549
+ continue;
3550
+ }
3551
+ currentBytes + sectionBytes > input.maxBytes && (parts.push(current), current = header, currentBytes = headerBytes), current += section, currentBytes += sectionBytes;
3552
+ }
3553
+ if (oversized.length) {
3554
+ let artifactsByPath = /* @__PURE__ */ new Map();
3555
+ for (let artifact of input.artifacts)
3556
+ artifactsByPath.has(artifact.markdownPath) || artifactsByPath.set(artifact.markdownPath, artifact);
3557
+ let links = oversized.map(({ markdownPath, url }) => {
3558
+ let artifact = artifactsByPath.get(markdownPath);
3559
+ return `- ${markdownLink2({
3560
+ title: artifact?.title ?? markdownPath,
3561
+ url
3562
+ })}`;
3563
+ }), omittedHeader = `## Pages omitted from this bundle
3564
+
3565
+ `, omitted = omittedHeader, omittedBytes = byteLength(omittedHeader);
3566
+ for (let link of links) {
3567
+ let linkWithNewline = `${link}
3568
+ `, linkBytes = byteLength(linkWithNewline);
3569
+ if (headerBytes + omittedBytes + linkBytes > input.maxBytes)
3570
+ throw new RangeError(
3571
+ "LLMS full-content omission links exceed the byte limit."
3572
+ );
3573
+ currentBytes + omittedBytes + linkBytes > input.maxBytes && (parts.push(current), current = header, currentBytes = headerBytes, omitted = omittedHeader, omittedBytes = byteLength(omittedHeader)), omitted += linkWithNewline, omittedBytes += linkBytes;
3574
+ }
3575
+ current += omitted, currentBytes += omittedBytes;
3576
+ }
3577
+ return (current !== header || parts.length === 0) && parts.push(current), { parts, oversized };
3578
+ }
3579
+
3580
+ // src/lib/runtime/metadata/llms-index-partition.ts
3581
+ var LLMS_INDEX_MAX_LINKS = 5e4, LLMS_INDEX_MAX_BYTES = 52428800;
3582
+ function partitionLlmsIndex(input) {
3583
+ let maxLinks = input.maxLinks ?? LLMS_INDEX_MAX_LINKS, maxBytes = input.maxBytes ?? LLMS_INDEX_MAX_BYTES;
3584
+ if (!Number.isSafeInteger(maxLinks) || maxLinks < 1)
3585
+ throw new RangeError("LLMS index link limit must be a positive integer.");
3586
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1)
3587
+ throw new RangeError("LLMS index byte limit must be a positive integer.");
3588
+ let sections = [...input.sections ?? []];
3589
+ input.otherGroupLinks?.length && sections.push({
3590
+ title: "Other content groups",
3591
+ entries: input.otherGroupLinks
3592
+ });
3593
+ let encoder = new TextEncoder(), bytes2 = (value) => encoder.encode(value).byteLength, serialize = (value) => serializeLlmsIndex({
3594
+ title: input.title,
3595
+ summary: input.summary,
3596
+ sections: value,
3597
+ includeDescriptions: input.includeDescriptions
3598
+ }), emptyIndex = serialize([]), header = emptyIndex.slice(0, -1), emptyIndexBytes = bytes2(emptyIndex);
3599
+ if (emptyIndexBytes > maxBytes)
3600
+ throw new RangeError("LLMS index header exceeds the byte limit.");
3601
+ let sectionPrefix = (title) => serialize([{ title, entries: [] }]).slice(header.length, -1), entryLine = (title, prefix, entry) => serialize([{ title, entries: [entry] }]).slice(header.length + prefix.length, -1), result = [], currentSections = [], currentBytes = emptyIndexBytes, currentLinks = 0, currentSourceSection = -1, finishPart = () => {
3602
+ let number = result.length + 1;
3603
+ result.push({
3604
+ name: `part-${String(number).padStart(3, "0")}`,
3605
+ number,
3606
+ linkCount: currentLinks,
3607
+ byteLength: currentBytes,
3608
+ sections: currentSections
3609
+ }), currentSections = [], currentBytes = emptyIndexBytes, currentLinks = 0, currentSourceSection = -1;
3610
+ };
3611
+ for (let [sectionIndex, section] of sections.entries()) {
3612
+ let prefix = sectionPrefix(section.title);
3613
+ if (section.entries.length === 0) {
3614
+ let sectionBytes = bytes2(
3615
+ `${currentSections.length ? `
3616
+
3617
+ ` : ""}${prefix}`
3618
+ );
3619
+ if (currentBytes + sectionBytes > maxBytes && currentSections.length && finishPart(), currentBytes + sectionBytes > maxBytes)
3620
+ throw new RangeError("LLMS index section exceeds the byte limit.");
3621
+ currentBytes += sectionBytes, currentSections.push({ title: section.title, entries: [] }), currentSourceSection = sectionIndex;
3622
+ continue;
3623
+ }
3624
+ for (let entry of section.entries) {
3625
+ let sameSection = currentSourceSection === sectionIndex, line = entryLine(section.title, prefix, entry), linkCount = entry.alternate ? 2 : 1, addition = `${sameSection ? `
3626
+ ` : currentSections.length ? `
3627
+
3628
+ ` : ""}${sameSection ? "" : prefix}${line}`, addedBytes = bytes2(addition), mustSplit = (currentLinks + linkCount > maxLinks || currentBytes + addedBytes > maxBytes) && currentSections.length > 0;
3629
+ mustSplit && finishPart();
3630
+ let candidateBytes = mustSplit ? bytes2(`${currentSections.length ? `
3631
+
3632
+ ` : ""}${prefix}${line}`) : addedBytes;
3633
+ if (currentLinks + linkCount > maxLinks || currentBytes + candidateBytes > maxBytes)
3634
+ throw new RangeError("LLMS index entry exceeds the part limits.");
3635
+ currentBytes += candidateBytes, currentLinks += linkCount;
3636
+ let destination = currentSourceSection === sectionIndex ? currentSections.at(-1) : void 0;
3637
+ destination ? destination.entries.push(entry) : currentSections.push({ title: section.title, entries: [entry] }), currentSourceSection = sectionIndex;
4283
3638
  }
4284
- sections.push(section), totalBytes += sectionBytes, included += 1;
4285
3639
  }
4286
- let footer = truncated ? `
4287
- > Truncated at ${included} of ${input.totalCount ?? input.artifacts.length} Markdown pages to stay under ${input.maxBytes} bytes.
4288
- ` : "";
4289
- return `${header}${sections.join("")}${footer}`;
3640
+ return (currentSections.length || result.length === 0) && finishPart(), { parts: result, split: result.length > 1 };
3641
+ }
3642
+ function serializePartitionedLlmsIndex(input) {
3643
+ let partitioned = partitionLlmsIndex(input), serialize = (sections) => serializeLlmsIndex({
3644
+ title: input.title,
3645
+ summary: input.summary,
3646
+ sections,
3647
+ includeDescriptions: input.includeDescriptions
3648
+ });
3649
+ if (!partitioned.split)
3650
+ return {
3651
+ body: serialize(partitioned.parts[0]?.sections ?? []),
3652
+ parts: []
3653
+ };
3654
+ let parts = partitioned.parts.map((part) => {
3655
+ let path = llmsPartPath(input.path, part.name);
3656
+ return { name: part.name, path, body: serialize(part.sections) };
3657
+ }), gateway = serialize([
3658
+ {
3659
+ title: "Parts",
3660
+ entries: parts.map((part, index) => ({
3661
+ title: `Part ${index + 1}`,
3662
+ url: input.partUrl?.(part.path) ?? part.path
3663
+ }))
3664
+ }
3665
+ ]), gatewayLinks = parts.length, gatewayBytes = new TextEncoder().encode(gateway).byteLength;
3666
+ if (gatewayLinks > (input.maxLinks ?? LLMS_INDEX_MAX_LINKS) || gatewayBytes > (input.maxBytes ?? LLMS_INDEX_MAX_BYTES))
3667
+ throw new RangeError(
3668
+ `LLMS index gateway "${input.path}" exceeds ${input.maxLinks ?? LLMS_INDEX_MAX_LINKS} links or ${input.maxBytes ?? LLMS_INDEX_MAX_BYTES} UTF-8 bytes.`
3669
+ );
3670
+ return { body: gateway, parts };
3671
+ }
3672
+
3673
+ // src/lib/runtime/metadata/sitemap-partitions.ts
3674
+ function sitemapXmlPartitions(partitions) {
3675
+ return partitions.filter((partition) => partition.xmlUrlCount > 0);
3676
+ }
3677
+ function bytes(value) {
3678
+ return Buffer.byteLength(value, "utf8");
3679
+ }
3680
+ function emptyPartition() {
3681
+ return {
3682
+ items: [],
3683
+ xmlItems: [],
3684
+ markdownItems: [],
3685
+ xmlUrlCount: 0,
3686
+ markdownUrlCount: 0,
3687
+ xmlFragmentBytes: 0,
3688
+ markdownFragmentBytes: 0
3689
+ };
3690
+ }
3691
+ function size(partition, options) {
3692
+ return {
3693
+ xmlBytes: partition.xmlUrlCount ? bytes(options.xmlPrefix) + bytes(options.xmlSuffix) + partition.xmlFragmentBytes : 0,
3694
+ markdownBytes: partition.items.length ? bytes(options.markdownPrefix) + bytes(options.markdownSuffix) + partition.markdownFragmentBytes : 0
3695
+ };
3696
+ }
3697
+ function exceededAfter(partition, item, options) {
3698
+ let xmlUrlCount = partition.xmlUrlCount + item.xmlUrls, markdownUrlCount = partition.markdownUrlCount + (item.markdownUrls ?? 0);
3699
+ if (xmlUrlCount > (options.maxXmlUrls ?? 5e4))
3700
+ return "URL limit";
3701
+ if ((xmlUrlCount ? bytes(options.xmlPrefix) + bytes(options.xmlSuffix) + partition.xmlFragmentBytes + (item.xml === null ? 0 : bytes(item.xml)) : 0) > (options.maxXmlBytes ?? 52428800))
3702
+ return "XML byte limit";
3703
+ if (bytes(options.markdownPrefix) + bytes(options.markdownSuffix) + partition.markdownFragmentBytes + bytes(item.markdown) > (options.maxMarkdownBytes ?? 52428800))
3704
+ return "Markdown byte limit";
3705
+ if (markdownUrlCount > (options.maxMarkdownUrls ?? 5e4))
3706
+ return "Markdown URL limit";
3707
+ }
3708
+ function add(partition, item) {
3709
+ partition.items.push(item.value), partition.markdownItems.push(item.value), partition.markdownFragmentBytes += bytes(item.markdown), partition.markdownUrlCount += item.markdownUrls ?? 0, item.xml !== null && (partition.xmlItems.push(item.value), partition.xmlFragmentBytes += bytes(item.xml)), partition.xmlUrlCount += item.xmlUrls;
3710
+ }
3711
+ function isValidItem(item) {
3712
+ return Number.isSafeInteger(item.xmlUrls) && item.xmlUrls >= 0 && (item.markdownUrls === void 0 || Number.isSafeInteger(item.markdownUrls) && item.markdownUrls >= 0) && (item.xml !== null || item.xmlUrls === 0);
3713
+ }
3714
+ function partitionSitemapEntries(options) {
3715
+ let partitions = [], current = emptyPartition();
3716
+ function flush() {
3717
+ if (current.items.length === 0) return;
3718
+ let measured = size(current, options);
3719
+ partitions.push({
3720
+ items: current.items,
3721
+ xmlItems: current.xmlItems,
3722
+ markdownItems: current.markdownItems,
3723
+ xmlUrlCount: current.xmlUrlCount,
3724
+ markdownUrlCount: current.markdownUrlCount,
3725
+ ...measured
3726
+ }), current = emptyPartition();
3727
+ }
3728
+ for (let [index, item] of options.items.entries()) {
3729
+ if (!isValidItem(item))
3730
+ throw new TypeError(
3731
+ `Sitemap entry ${index + 1} must have a non-negative URL count and XML output when it contributes URLs.`
3732
+ );
3733
+ let limit = exceededAfter(current, item, options);
3734
+ if (limit && current.items.length > 0 && (flush(), limit = exceededAfter(current, item, options)), limit)
3735
+ throw new RangeError(`Sitemap entry ${index + 1} exceeds the ${limit}.`);
3736
+ add(current, item);
3737
+ }
3738
+ return flush(), partitions;
4290
3739
  }
4291
3740
 
4292
3741
  // src/lib/compiler/nextjs/compose.ts
4293
3742
  var BANNER = `// AUTO-GENERATED by the PublishOS framework - do not edit.
4294
- `, MARKDOWN_ARTIFACT_SHARD_MAX_BYTES = 256e3, MARKDOWN_ARTIFACT_SHARD_MAX_PAGES = 250, DEFAULT_LLMS_FULL_MAX_BYTES = 1e6;
3743
+ `, MARKDOWN_ARTIFACT_SHARD_MAX_BYTES = 256e3, MARKDOWN_ARTIFACT_SHARD_MAX_PAGES = 250;
4295
3744
  function isRootContentGroup(groupKey) {
4296
3745
  return groupKey === ROOT_CONTENT_GROUP_KEY;
4297
3746
  }
@@ -5328,9 +4777,10 @@ async function overlaySource(publishOsDir, sourceDir, config, fonts, theme, page
5328
4777
  for (let file of groupSourceFiles) {
5329
4778
  let projectPath = resolve7(group.folderPath, file), dest = resolve7(sourceDir, sourceGroup, file);
5330
4779
  await fileExists5(projectPath) ? (await copyTreeIfExists(projectPath, dest), sourceOverrides.push(`${sourceGroup}/${file}`)) : file === "layout.tsx" && (await Promise.all(
5331
- (await resolveLayoutChain(config, groupKey)).filter((entry) => entry.key !== groupKey).map(
5332
- (entry) => fileExists5(resolve7(config.groups[entry.key].folderPath, file))
5333
- )
4780
+ (await resolveLayoutChain(config, groupKey)).filter((entry) => entry.key !== groupKey).map((entry) => {
4781
+ let ancestor = config.groups[entry.key];
4782
+ return ancestor ? fileExists5(resolve7(ancestor.folderPath, file)) : !1;
4783
+ })
5334
4784
  )).some(Boolean) ? await writeText(
5335
4785
  dest,
5336
4786
  `import type { LayoutProps } from "@publish.os/nextjs/types";
@@ -5364,7 +4814,7 @@ export default function InheritedGroupLayout({ children }: LayoutProps) { return
5364
4814
  function manifestModule(manifest) {
5365
4815
  return `import type { ManifestLike } from "@publish.os/nextjs/types";
5366
4816
 
5367
- const manifest = ${JSON.stringify(manifest, null, 2)} satisfies ManifestLike;
4817
+ const manifest = JSON.parse(${JSON.stringify(JSON.stringify(manifest))}) as ManifestLike;
5368
4818
 
5369
4819
  export default manifest;
5370
4820
  `;
@@ -5435,6 +4885,37 @@ function outputPathWithinGroup(path, groupPath) {
5435
4885
  function outputDirectory2(path) {
5436
4886
  return path.slice(0, path.lastIndexOf("/") + 1) || "/";
5437
4887
  }
4888
+ function isWithinOutputDirectory(path, directories) {
4889
+ for (let end = path.indexOf("/"); end >= 0; end = path.indexOf("/", end + 1))
4890
+ if (directories.has(path.slice(0, end + 1))) return !0;
4891
+ return !1;
4892
+ }
4893
+ function outputDirectoryPrefixes(path) {
4894
+ let directories = [];
4895
+ for (let end = path.indexOf("/"); end >= 0; end = path.indexOf("/", end + 1))
4896
+ directories.push(path.slice(0, end + 1));
4897
+ return directories;
4898
+ }
4899
+ function parentOutputDirectory(directory) {
4900
+ return directory === "/" ? void 0 : outputDirectory2(directory.slice(0, -1));
4901
+ }
4902
+ function sitemapLeavesByOriginDirectory(manifest, leaves) {
4903
+ let index = /* @__PURE__ */ new Map();
4904
+ for (let leaf of leaves) {
4905
+ let origin = effectiveOrigin(manifest, leaf.group);
4906
+ for (let directory of outputDirectoryPrefixes(leaf.path)) {
4907
+ let key = JSON.stringify([origin ?? null, directory]), matchingLeaves = index.get(key) ?? [];
4908
+ matchingLeaves.push(leaf), index.set(key, matchingLeaves);
4909
+ }
4910
+ }
4911
+ return index;
4912
+ }
4913
+ function addSitemapEntryByDirectory(index, origin, path, entry) {
4914
+ for (let directory of outputDirectoryPrefixes(path)) {
4915
+ let key = JSON.stringify([origin ?? null, directory]), entries = index.get(key) ?? [];
4916
+ entries.push(entry), index.set(key, entries);
4917
+ }
4918
+ }
5438
4919
  function discoveryPlanCovers({
5439
4920
  manifest,
5440
4921
  plan,
@@ -5447,18 +4928,25 @@ function discoveryPlanCovers({
5447
4928
  return scopeGroup ? outputPathWithinGroup(path, manifest.groups[scopeGroup]?.path ?? "/") ? plan.scope === "site" ? !0 : group === scopeGroup || plan.scope !== "self" && group.startsWith(`${scopeGroup}/`) : !1 : !0;
5448
4929
  }
5449
4930
  function outputFactory(kind) {
5450
- return kind === "atom" ? { factory: "createAtomFeedRoute", routeModule: "feed" } : kind === "json-feed" ? { factory: "createJsonFeedRoute", routeModule: "feed" } : kind === "rss" ? { factory: "createRssFeedRoute", routeModule: "feed" } : kind === "llms" ? { factory: "createLlmsTxtRoute", routeModule: "llms" } : kind === "llms-full" ? { factory: "createLlmsFullTxtRoute", routeModule: "llms" } : kind === "sitemap-md" ? { factory: "createSitemapMarkdownRoute", routeModule: "sitemap" } : { factory: "createSitemapXmlRoute", routeModule: "sitemap" };
4931
+ return kind === "atom" ? { factory: "createAtomFeedRoute", routeModule: "feed" } : kind === "json-feed" ? { factory: "createJsonFeedRoute", routeModule: "feed" } : kind === "rss" ? { factory: "createRssFeedRoute", routeModule: "feed" } : kind === "sitemap-md" ? { factory: "createSitemapMarkdownRoute", routeModule: "sitemap" } : { factory: "createSitemapXmlRoute", routeModule: "sitemap" };
5451
4932
  }
5452
4933
  function outputRouteModule(plan) {
5453
4934
  let { factory, routeModule } = outputFactory(plan.kind);
5454
- if (plan.kind === "robots" || plan.kind === "llms" || plan.kind === "llms-full")
4935
+ if (plan.kind === "robots")
4936
+ return `import { agentTextOutputs, agentTextOutputsByOrigin } from "@source/.publishos/agent-text.generated";
4937
+ import { createRobotsTxtRoute } from "@publish.os/nextjs/kernel/routes/metadata/robots";
4938
+
4939
+ export const GET = createRobotsTxtRoute(agentTextOutputs[${JSON.stringify(plan.path)}], agentTextOutputsByOrigin);
4940
+ export const HEAD = GET;
4941
+ `;
4942
+ if (plan.kind === "llms" || plan.kind === "llms-full")
5455
4943
  return `import { agentTextOutputs } from "@source/.publishos/agent-text.generated";
5456
- import { createPrecomputedLlmsTxtRoute } from "@publish.os/nextjs/kernel/routes/metadata/llms";
4944
+ import { createLlmsTxtRoute } from "@publish.os/nextjs/kernel/routes/metadata/llms";
5457
4945
 
5458
- export const GET = createPrecomputedLlmsTxtRoute(agentTextOutputs[${JSON.stringify(plan.path)}]);
4946
+ export const GET = createLlmsTxtRoute(agentTextOutputs[${JSON.stringify(plan.path)}]);
5459
4947
  export const HEAD = GET;
5460
4948
  `;
5461
- if (plan.kind === "rss" || plan.kind === "atom" || plan.kind === "json-feed")
4949
+ if (plan.kind === "rss" || plan.kind === "json-feed")
5462
4950
  return `import { agentTextOutputs } from "@source/.publishos/agent-text.generated";
5463
4951
  import { createPrecomputedFeedRoute } from "@publish.os/nextjs/kernel/routes/metadata/feed";
5464
4952
 
@@ -5466,7 +4954,7 @@ export const GET = createPrecomputedFeedRoute(agentTextOutputs[${JSON.stringify(
5466
4954
  export const HEAD = GET;
5467
4955
  `;
5468
4956
  let options = {};
5469
- plan.scope && (options.scope = plan.scope), plan.group && (options.group = plan.group), plan.kind === "sitemap-md" && plan.scopeGroup && (options.group = plan.scopeGroup), plan.limit && (options.limit = plan.limit), plan.status && (options.status = plan.status), plan.includeDescriptions && (options.includeDescriptions = plan.includeDescriptions), plan.links && (options.links = plan.links), plan.otherGroupLinks && (options.otherGroupLinks = plan.otherGroupLinks), plan.kind === "sitemap" && (options.path = plan.path, plan.sitemaps && (options.sitemaps = plan.sitemaps), plan.exclude?.length && (options.exclude = plan.exclude)), plan.markdownAlternates === !1 && (options.markdownAlternates = !1);
4957
+ plan.scope && (options.scope = plan.scope), plan.group && (options.group = plan.group), plan.kind === "sitemap-md" && plan.scopeGroup && (options.group = plan.scopeGroup), plan.limit && (options.limit = plan.limit), plan.includeDescriptions && (options.includeDescriptions = plan.includeDescriptions), plan.links && (options.links = plan.links), plan.otherGroupLinks && (options.otherGroupLinks = plan.otherGroupLinks), plan.kind === "sitemap-md" && (plan.groupSitemaps && (options.groupSitemaps = plan.groupSitemaps), plan.parts && (options.parts = plan.parts), plan.excludeGroups && (options.excludeGroups = plan.excludeGroups), plan.includeUrls && (options.includeUrls = plan.includeUrls)), plan.kind === "sitemap" && (options.path = plan.path, plan.sitemaps && (options.sitemaps = plan.sitemaps), plan.exclude?.length && (options.exclude = plan.exclude), plan.includeUrls && (options.includeUrls = plan.includeUrls)), plan.markdownAlternates === !1 && (options.markdownAlternates = !1);
5470
4958
  let args = Object.keys(options).length > 0 ? `manifest, ${JSON.stringify(options, null, 2)}` : "manifest";
5471
4959
  return plan.kind === "sitemap-md" ? `import manifest from "@manifest";
5472
4960
  import { markdownArtifactManifest } from "@source/.publishos/markdown-index.generated";
@@ -5483,6 +4971,408 @@ function markdownRoutePathForPage(pagePath) {
5483
4971
  let normalized = normalizeOutputPath(pagePath);
5484
4972
  return normalized === "/" ? "/index.md" : `${normalized}.md`;
5485
4973
  }
4974
+ function sitemapPartPath(path, partIndex) {
4975
+ return `${path.replace(/\.xml$/, "")}-part-${String(partIndex + 1).padStart(3, "0")}.xml`;
4976
+ }
4977
+ function sitemapIndexPartPath(path, partIndex) {
4978
+ return `${path.replace(/\.xml$/, "")}-index-part-${String(partIndex + 1).padStart(3, "0")}.xml`;
4979
+ }
4980
+ function sitemapMarkdownPartPath(path, partIndex) {
4981
+ return `${path.replace(/\.md$/, "")}-part-${String(partIndex + 1).padStart(3, "0")}.md`;
4982
+ }
4983
+ function sitemapMarkdownGroupPartPath(path, partIndex) {
4984
+ return `${path.replace(/\.md$/, "")}-groups-part-${String(partIndex + 1).padStart(3, "0")}.md`;
4985
+ }
4986
+ function sitemapXmlEscape(value) {
4987
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/'/g, "&apos;").replace(/"/g, "&quot;");
4988
+ }
4989
+ function sitemapLeafEntries(manifest, plan, groupsByOriginDirectory, pagesByOriginDirectory, rootMountedGroups) {
4990
+ let directory = outputDirectory2(plan.path), origin = effectiveOrigin(manifest, plan.group), rootMountedGroup = rootMountedGroups.get(origin), key = JSON.stringify([origin ?? null, directory]), excludedDirectories = new Set(
4991
+ (plan.exclude ?? []).filter((sitemap) => effectiveOrigin(manifest, sitemap.group) === origin).map((sitemap) => outputDirectory2(sitemap.path))
4992
+ ), urls = [
4993
+ {
4994
+ path: (plan.group ? manifest.groups[plan.group] : rootMountedGroup)?.path ?? "/",
4995
+ group: (plan.group ? manifest.groups[plan.group] : rootMountedGroup)?.key
4996
+ },
4997
+ ...(groupsByOriginDirectory.get(key) ?? []).map((group) => ({
4998
+ path: group.path,
4999
+ group: group.key
5000
+ })),
5001
+ ...(pagesByOriginDirectory.get(key) ?? []).map((page) => ({
5002
+ path: page.path,
5003
+ group: page.group,
5004
+ xmlAllowed: page.frontmatter.metadata.robots?.index !== !1
5005
+ }))
5006
+ ].filter(
5007
+ ({ path }) => (directory === "/" || path.startsWith(directory)) && !isWithinOutputDirectory(path, excludedDirectories)
5008
+ ), unique = /* @__PURE__ */ new Map();
5009
+ for (let { path, group, xmlAllowed = !0 } of urls)
5010
+ unique.set(canonicalUrl(manifest, path, group), {
5011
+ reference: { path, group },
5012
+ xmlAllowed
5013
+ });
5014
+ return [...unique.values()];
5015
+ }
5016
+ function expandSitemapOutputPlans(manifest, outputPlans, availableMarkdownPaths) {
5017
+ let expanded = [], groupsByOriginDirectory = /* @__PURE__ */ new Map(), pagesByOriginDirectory = /* @__PURE__ */ new Map(), rootMountedGroups = /* @__PURE__ */ new Map();
5018
+ for (let group of Object.values(manifest.groups)) {
5019
+ let origin = effectiveOrigin(manifest, group.key);
5020
+ addSitemapEntryByDirectory(
5021
+ groupsByOriginDirectory,
5022
+ origin,
5023
+ group.path,
5024
+ group
5025
+ ), group.key !== ROOT_CONTENT_GROUP_KEY && group.path === "/" && !rootMountedGroups.has(origin) && rootMountedGroups.set(origin, group);
5026
+ }
5027
+ for (let page of manifest.pages)
5028
+ page.frontmatter.status === "published" && addSitemapEntryByDirectory(
5029
+ pagesByOriginDirectory,
5030
+ effectiveOrigin(manifest, page.group),
5031
+ page.path,
5032
+ page
5033
+ );
5034
+ let markdownPartsByGroup = /* @__PURE__ */ new Map(), urlPrefix = `<?xml version="1.0" encoding="UTF-8"?>
5035
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
5036
+ `, urlSuffix = `</urlset>
5037
+ `, markdownPaths = new Map(
5038
+ manifest.pages.map((page) => [page.path, markdownRoutePathForPage(page.path)]).filter(([, path]) => availableMarkdownPaths.has(path))
5039
+ ), markdownPlansByGroup = /* @__PURE__ */ new Map();
5040
+ for (let candidate of outputPlans)
5041
+ candidate.kind === "sitemap-md" && !markdownPlansByGroup.has(candidate.group) && markdownPlansByGroup.set(candidate.group, candidate);
5042
+ for (let plan of outputPlans) {
5043
+ if (plan.kind !== "sitemap" || plan.sitemaps !== void 0) {
5044
+ expanded.push(plan);
5045
+ continue;
5046
+ }
5047
+ let entries = sitemapLeafEntries(
5048
+ manifest,
5049
+ plan,
5050
+ groupsByOriginDirectory,
5051
+ pagesByOriginDirectory,
5052
+ rootMountedGroups
5053
+ ), matchingMarkdownPlan = markdownPlansByGroup.get(plan.group), markdownByReference = /* @__PURE__ */ new Map(), navigationOrder = /* @__PURE__ */ new Map(), sitemapTitle = `${manifest.groups[plan.group ?? ""]?.title ?? manifest.site.name} Sitemap`, sections = navigationDiscoverySections({
5054
+ manifest,
5055
+ group: matchingMarkdownPlan?.scopeGroup ?? matchingMarkdownPlan?.group,
5056
+ scope: matchingMarkdownPlan?.scope,
5057
+ limit: matchingMarkdownPlan?.limit,
5058
+ excludeGroups: matchingMarkdownPlan?.excludeGroups,
5059
+ markdownPaths
5060
+ });
5061
+ for (let section of sections)
5062
+ for (let entry of section.entries) {
5063
+ let reference = entry.type === "page" ? { path: entry.path, group: entry.group } : (() => {
5064
+ let group = manifest.groups[entry.group];
5065
+ if (group && canonicalUrl(manifest, group.path, group.key) === entry.url)
5066
+ return { path: group.path, group: group.key };
5067
+ })();
5068
+ if (!reference) continue;
5069
+ let htmlUrl = canonicalUrl(manifest, reference.path, reference.group), markdownPath = markdownPaths.get(reference.path), fragment = serializeLlmsIndex({
5070
+ title: sitemapTitle,
5071
+ summary: "Browse the published pages and their available Markdown versions.",
5072
+ sections: [
5073
+ {
5074
+ title: section.title,
5075
+ entries: [
5076
+ {
5077
+ title: entry.title,
5078
+ url: htmlUrl,
5079
+ ...markdownPath ? {
5080
+ alternate: {
5081
+ title: "Markdown",
5082
+ url: canonicalUrl(
5083
+ manifest,
5084
+ markdownPath,
5085
+ reference.group
5086
+ )
5087
+ }
5088
+ } : {}
5089
+ }
5090
+ ]
5091
+ }
5092
+ ]
5093
+ });
5094
+ markdownByReference.set(
5095
+ JSON.stringify([reference.path, reference.group]),
5096
+ { fragment, count: 1 + +!!markdownPath }
5097
+ ), navigationOrder.has(
5098
+ JSON.stringify([reference.path, reference.group])
5099
+ ) || navigationOrder.set(
5100
+ JSON.stringify([reference.path, reference.group]),
5101
+ navigationOrder.size
5102
+ );
5103
+ }
5104
+ let orderedEntries = entries.map((entry, index) => ({
5105
+ ...entry,
5106
+ index,
5107
+ order: navigationOrder.get(
5108
+ JSON.stringify([entry.reference.path, entry.reference.group])
5109
+ ) ?? Number.MAX_SAFE_INTEGER
5110
+ })).sort((a, b) => a.order - b.order || a.index - b.index), partitions = partitionSitemapEntries({
5111
+ items: orderedEntries.map(({ reference, xmlAllowed }) => {
5112
+ let url = canonicalUrl(manifest, reference.path, reference.group), markdown = markdownByReference.get(
5113
+ JSON.stringify([reference.path, reference.group])
5114
+ );
5115
+ return {
5116
+ value: reference,
5117
+ xml: xmlAllowed ? ` <url><loc>${sitemapXmlEscape(url)}</loc></url>
5118
+ ` : null,
5119
+ xmlUrls: Number(xmlAllowed),
5120
+ markdownUrls: markdown?.count ?? 0,
5121
+ markdown: markdown?.fragment ?? ""
5122
+ };
5123
+ }),
5124
+ xmlPrefix: urlPrefix,
5125
+ xmlSuffix: urlSuffix,
5126
+ markdownPrefix: "",
5127
+ markdownSuffix: ""
5128
+ }), xmlPartitions = sitemapXmlPartitions(partitions);
5129
+ if (xmlPartitions.length === 0) continue;
5130
+ if (xmlPartitions.length <= 1) {
5131
+ expanded.push(plan), partitions.length > 1 && markdownPartsByGroup.set(
5132
+ plan.group ?? ROOT_CONTENT_GROUP_KEY,
5133
+ partitions.map((partition) => partition.markdownItems)
5134
+ );
5135
+ continue;
5136
+ }
5137
+ let parts = xmlPartitions.map((_partition, index) => ({
5138
+ path: sitemapPartPath(plan.path, index),
5139
+ ...plan.group ? { group: plan.group } : {}
5140
+ }));
5141
+ markdownPartsByGroup.set(
5142
+ plan.group ?? ROOT_CONTENT_GROUP_KEY,
5143
+ partitions.map((partition) => partition.markdownItems)
5144
+ ), expanded.push({
5145
+ ...plan,
5146
+ sitemaps: parts,
5147
+ allUrls: void 0,
5148
+ includeUrls: void 0,
5149
+ exclude: void 0
5150
+ }), xmlPartitions.forEach((partition, index) => {
5151
+ expanded.push({
5152
+ ...plan,
5153
+ path: parts[index]?.path ?? sitemapPartPath(plan.path, index),
5154
+ sitemaps: void 0,
5155
+ exclude: void 0,
5156
+ allUrls: void 0,
5157
+ includeUrls: partition.xmlItems
5158
+ });
5159
+ });
5160
+ }
5161
+ let xmlPlans = expanded.filter((plan) => plan.kind === "sitemap"), leaves = xmlPlans.filter((plan) => plan.sitemaps === void 0), leavesByOriginDirectory = sitemapLeavesByOriginDirectory(
5162
+ manifest,
5163
+ leaves
5164
+ );
5165
+ for (let index of xmlPlans.filter((plan) => plan.sitemaps !== void 0)) {
5166
+ let directory = outputDirectory2(index.path), key = JSON.stringify([
5167
+ effectiveOrigin(manifest, index.group) ?? null,
5168
+ directory
5169
+ ]);
5170
+ index.sitemaps = (leavesByOriginDirectory.get(key) ?? []).filter((leaf) => leaf.path !== index.path).map(({ path, group }) => ({ path, ...group ? { group } : {} }));
5171
+ }
5172
+ let indexedPlans = [], indexPrefix = `<?xml version="1.0" encoding="UTF-8"?>
5173
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
5174
+ `, indexSuffix = `</sitemapindex>
5175
+ `;
5176
+ for (let plan of expanded) {
5177
+ if (plan.kind !== "sitemap" || plan.sitemaps === void 0) {
5178
+ indexedPlans.push(plan);
5179
+ continue;
5180
+ }
5181
+ let partitions = partitionSitemapEntries({
5182
+ items: plan.sitemaps.map((sitemap) => ({
5183
+ value: sitemap,
5184
+ xml: ` <sitemap><loc>${sitemapXmlEscape(canonicalUrl(manifest, sitemap.path, sitemap.group))}</loc></sitemap>
5185
+ `,
5186
+ xmlUrls: 1,
5187
+ markdown: ""
5188
+ })),
5189
+ xmlPrefix: indexPrefix,
5190
+ xmlSuffix: indexSuffix,
5191
+ markdownPrefix: "",
5192
+ markdownSuffix: ""
5193
+ });
5194
+ if (partitions.length <= 1) {
5195
+ indexedPlans.push(plan);
5196
+ continue;
5197
+ }
5198
+ if (!expanded.some((candidate) => candidate.kind === "robots"))
5199
+ throw new AppError({
5200
+ code: "FRAMEWORK_OUTPUT_PATH_INVALID",
5201
+ message: `Sitemap index "${plan.path}" exceeds the protocol limit and needs independent index files, but robots output is disabled.`,
5202
+ fix: "Enable root robots output so the compiler can advertise every index file for its origin.",
5203
+ details: {
5204
+ path: plan.path,
5205
+ origin: effectiveOrigin(manifest, plan.group),
5206
+ indexCount: partitions.length
5207
+ }
5208
+ });
5209
+ partitions.forEach((partition, index) => {
5210
+ indexedPlans.push({
5211
+ ...plan,
5212
+ ...index === 0 ? {} : {
5213
+ path: sitemapIndexPartPath(plan.path, index),
5214
+ advertiseWithRobots: !0
5215
+ },
5216
+ sitemaps: partition.items
5217
+ });
5218
+ });
5219
+ }
5220
+ return indexedPlans.flatMap((plan) => {
5221
+ if (plan.kind !== "sitemap-md") return [plan];
5222
+ let sitemapPartitions = markdownPartsByGroup.get(
5223
+ plan.group ?? ROOT_CONTENT_GROUP_KEY
5224
+ ), markdownPaths2 = new Map(
5225
+ manifest.pages.map(
5226
+ (page) => [page.path, markdownRoutePathForPage(page.path)]
5227
+ ).filter(([, path]) => availableMarkdownPaths.has(path))
5228
+ ), sections = navigationDiscoverySections({
5229
+ manifest,
5230
+ group: plan.scopeGroup ?? plan.group,
5231
+ scope: plan.scope,
5232
+ limit: plan.limit,
5233
+ excludeGroups: plan.excludeGroups,
5234
+ markdownPaths: markdownPaths2
5235
+ }), parts;
5236
+ if (sitemapPartitions && sitemapPartitions.length > 1) {
5237
+ let visibleEntries = sections.flatMap((section) => section.entries), visiblePageKeys = /* @__PURE__ */ new Set(), visibleGroupKeys = /* @__PURE__ */ new Set();
5238
+ for (let entry of visibleEntries)
5239
+ entry.type === "page" ? visiblePageKeys.add(
5240
+ JSON.stringify([entry.group ?? null, entry.path])
5241
+ ) : visibleGroupKeys.add(
5242
+ JSON.stringify([entry.group ?? null, entry.url])
5243
+ );
5244
+ parts = sitemapPartitions.map(
5245
+ (references) => references.filter(
5246
+ (reference) => visiblePageKeys.has(
5247
+ JSON.stringify([reference.group ?? null, reference.path])
5248
+ ) || reference.path !== void 0 && visibleGroupKeys.has(
5249
+ JSON.stringify([
5250
+ reference.group ?? null,
5251
+ canonicalUrl(manifest, reference.path, reference.group)
5252
+ ])
5253
+ )
5254
+ )
5255
+ ).filter((references) => references.length > 0);
5256
+ } else {
5257
+ let title = `${manifest.groups[plan.group ?? plan.scopeGroup ?? ""]?.title ?? manifest.site.name} Sitemap`, candidates = sections.flatMap(
5258
+ (section) => section.entries.map((entry) => {
5259
+ let reference = entry.type === "page" ? { path: entry.path, group: entry.group } : { url: entry.url, group: entry.group }, alternatePath = entry.type === "page" ? markdownPaths2.get(entry.path) : void 0;
5260
+ return {
5261
+ reference,
5262
+ value: reference,
5263
+ xml: null,
5264
+ xmlUrls: 0,
5265
+ markdownUrls: 1 + +!!alternatePath,
5266
+ markdown: serializeLlmsIndex({
5267
+ title,
5268
+ summary: "Browse the published pages and their available Markdown versions.",
5269
+ sections: [
5270
+ {
5271
+ title: section.title,
5272
+ entries: [
5273
+ {
5274
+ title: entry.title,
5275
+ url: entry.url,
5276
+ ...alternatePath ? {
5277
+ alternate: {
5278
+ title: "Markdown",
5279
+ url: canonicalUrl(
5280
+ manifest,
5281
+ alternatePath,
5282
+ entry.group
5283
+ )
5284
+ }
5285
+ } : {}
5286
+ }
5287
+ ]
5288
+ }
5289
+ ]
5290
+ })
5291
+ };
5292
+ })
5293
+ );
5294
+ if (parts = partitionSitemapEntries({
5295
+ items: candidates,
5296
+ xmlPrefix: "",
5297
+ xmlSuffix: "",
5298
+ markdownPrefix: "",
5299
+ markdownSuffix: ""
5300
+ }).map((partition) => partition.items), parts.length < 2) return [plan];
5301
+ }
5302
+ if (parts.length === 0) return [plan];
5303
+ let links = parts.map((_, index) => ({
5304
+ title: `Part ${index + 1}`,
5305
+ path: sitemapMarkdownPartPath(plan.path, index),
5306
+ ...plan.group ? { group: plan.group } : {}
5307
+ }));
5308
+ return [
5309
+ { ...plan, limit: void 0, parts: links },
5310
+ ...parts.map((includeUrls, index) => ({
5311
+ ...plan,
5312
+ path: sitemapMarkdownPartPath(plan.path, index),
5313
+ groupSitemaps: [],
5314
+ parts: [],
5315
+ links: [],
5316
+ otherGroupLinks: [],
5317
+ limit: void 0,
5318
+ includeUrls
5319
+ }))
5320
+ ];
5321
+ }).flatMap((plan) => {
5322
+ if (plan.kind !== "sitemap-md" || !plan.groupSitemaps?.length)
5323
+ return [plan];
5324
+ let title = `${manifest.groups[plan.group ?? plan.scopeGroup ?? ""]?.title ?? manifest.site.name} Sitemap`, partitions = partitionSitemapEntries({
5325
+ items: plan.groupSitemaps.map((sitemap) => ({
5326
+ value: sitemap,
5327
+ xml: null,
5328
+ xmlUrls: 0,
5329
+ markdownUrls: 1,
5330
+ markdown: serializeLlmsIndex({
5331
+ title,
5332
+ summary: "Browse the published pages and their available Markdown versions.",
5333
+ sections: [
5334
+ {
5335
+ title: "Content groups",
5336
+ entries: [
5337
+ {
5338
+ title: sitemap.title,
5339
+ url: canonicalUrl(manifest, sitemap.path, sitemap.group)
5340
+ }
5341
+ ]
5342
+ }
5343
+ ]
5344
+ })
5345
+ })),
5346
+ xmlPrefix: "",
5347
+ xmlSuffix: "",
5348
+ markdownPrefix: "",
5349
+ markdownSuffix: ""
5350
+ });
5351
+ if (partitions.length <= 1) return [plan];
5352
+ let links = partitions.map((_partition, index) => ({
5353
+ title: `Group links ${index + 1}`,
5354
+ path: sitemapMarkdownGroupPartPath(plan.path, index),
5355
+ ...plan.group ? { group: plan.group } : {}
5356
+ }));
5357
+ return [
5358
+ {
5359
+ ...plan,
5360
+ groupSitemaps: [],
5361
+ parts: [...plan.parts ?? [], ...links]
5362
+ },
5363
+ ...partitions.map((partition, index) => ({
5364
+ ...plan,
5365
+ path: sitemapMarkdownGroupPartPath(plan.path, index),
5366
+ groupSitemaps: partition.items,
5367
+ parts: [],
5368
+ links: [],
5369
+ otherGroupLinks: [],
5370
+ includeUrls: [],
5371
+ limit: void 0
5372
+ }))
5373
+ ];
5374
+ });
5375
+ }
5486
5376
  function markdownContentHash(value) {
5487
5377
  return `sha256-${createHash4("sha256").update(value).digest("hex").slice(0, 32)}`;
5488
5378
  }
@@ -5506,44 +5396,59 @@ function llmsSummary(manifest, title) {
5506
5396
  return manifest.site.description ? `> ${manifest.site.description}` : `> Agent-readable index for ${title}.`;
5507
5397
  }
5508
5398
  function llmsTextBody(manifest, plan, markdownPaths) {
5509
- let home = outputHome(manifest, plan), pages = discoveryPages({
5399
+ let home = outputHome(manifest, plan), outputSections = navigationDiscoverySections({
5510
5400
  manifest,
5511
- ...plan,
5512
- group: plan.scopeGroup ?? plan.group
5513
- }), sections = groupDiscoveryPages({ manifest, pages });
5514
- for (let link of plan.links ?? []) {
5515
- let group = link.group ? manifest.groups[link.group] : void 0;
5516
- group && !sections.some((section) => section.group === group.key) && sections.push({ group: group.key, title: group.title, pages: [] });
5517
- }
5518
- return serializeLlmsIndex({
5519
- title: home.title,
5520
- summary: llmsSummary(manifest, home.title).replace(/^> /, ""),
5521
- sections: sections.sort(
5522
- (a, b) => (manifest.groups[a.group ?? ""]?.path ?? "/").localeCompare(
5523
- manifest.groups[b.group ?? ""]?.path ?? "/"
5524
- )
5525
- ).map((section) => ({
5526
- title: section.title,
5527
- pages: section.pages.map((page) => ({
5528
- title: page.title,
5529
- url: canonicalUrl(
5530
- manifest,
5531
- markdownPaths.get(page.path) ?? page.path,
5532
- page.group
5533
- ),
5534
- description: page.description
5535
- })),
5536
- links: plan.links?.filter((link) => link.group === section.group).map((link) => ({
5537
- title: `${link.title} index`,
5401
+ group: plan.scopeGroup ?? plan.group,
5402
+ scope: plan.scope,
5403
+ limit: plan.limit,
5404
+ markdownPaths
5405
+ }).map((section) => ({
5406
+ title: section.title,
5407
+ entries: section.entries.map((entry) => ({
5408
+ title: entry.title,
5409
+ url: entry.url,
5410
+ ...entry.type === "page" ? { description: entry.description } : {}
5411
+ }))
5412
+ }));
5413
+ for (let [title2, links] of [
5414
+ ["Group LLMS indexes", plan.links],
5415
+ ["Other content groups", plan.otherGroupLinks]
5416
+ ])
5417
+ links?.length && outputSections.push({
5418
+ title: title2,
5419
+ entries: links.map((link) => ({
5420
+ title: title2 === "Group LLMS indexes" ? `${link.title} index` : link.title,
5538
5421
  url: canonicalUrl(manifest, link.path, link.group)
5539
5422
  }))
5540
- })),
5541
- includeDescriptions: plan.includeDescriptions,
5542
- otherGroupLinks: plan.otherGroupLinks?.map((link) => ({
5543
- title: link.title,
5544
- url: canonicalUrl(manifest, link.path, link.group)
5545
- }))
5423
+ });
5424
+ let input = {
5425
+ title: home.title === manifest.site.name ? manifest.site.name : `${home.title} \u2014 ${manifest.site.name}`,
5426
+ summary: llmsSummary(manifest, home.title).replace(/^> /, ""),
5427
+ sections: outputSections,
5428
+ includeDescriptions: plan.includeDescriptions
5429
+ }, output = serializePartitionedLlmsIndex({
5430
+ ...input,
5431
+ path: plan.path,
5432
+ partUrl: (path) => canonicalUrl(manifest, path, plan.group)
5546
5433
  });
5434
+ return {
5435
+ body: output.body,
5436
+ partBodies: new Map(output.parts.map(({ path, body }) => [path, body]))
5437
+ };
5438
+ }
5439
+ function expandLlmsIndexOutputPlans(options) {
5440
+ let outputPlans = [], bodies = /* @__PURE__ */ new Map();
5441
+ for (let plan of options.outputPlans) {
5442
+ if (plan.kind !== "llms") {
5443
+ outputPlans.push(plan);
5444
+ continue;
5445
+ }
5446
+ let output = llmsTextBody(options.manifest, plan, options.markdownPaths);
5447
+ outputPlans.push(plan), bodies.set(plan.path, output.body);
5448
+ for (let [path, body] of output.partBodies)
5449
+ outputPlans.push({ ...plan, path }), bodies.set(path, body);
5450
+ }
5451
+ return { outputPlans, bodies };
5547
5452
  }
5548
5453
  function feedContentType(kind) {
5549
5454
  if (kind === "rss") return "application/rss+xml; charset=utf-8";
@@ -5551,16 +5456,11 @@ function feedContentType(kind) {
5551
5456
  if (kind === "json-feed") return "application/feed+json; charset=utf-8";
5552
5457
  }
5553
5458
  function pageTimestamp(page) {
5554
- let value = page.publishedAt ?? page.updatedAt;
5555
- if (!value) return -1 / 0;
5556
- let timestamp = Date.parse(value);
5557
- return Number.isNaN(timestamp) ? -1 / 0 : timestamp;
5558
- }
5559
- function outputStatuses(plan) {
5560
- return new Set(plan.status ?? ["published"]);
5459
+ let value = authoredDate(page.frontmatter.publishedAt) ?? authoredDate(page.frontmatter.updatedAt);
5460
+ return value ? Date.parse(value) : -1 / 0;
5561
5461
  }
5562
5462
  function feedPages(manifest, plan) {
5563
- let statuses = outputStatuses(plan), pages = manifest.pages.filter((page) => statuses.has(page.status)).filter((page) => pageInOutputScope(page, plan, manifest)).sort((a, b) => pageTimestamp(b) - pageTimestamp(a));
5463
+ let pages = manifest.pages.filter((page) => page.frontmatter.status === "published").filter((page) => pageInOutputScope(page, plan, manifest)).sort((a, b) => pageTimestamp(b) - pageTimestamp(a));
5564
5464
  return plan.limit ? pages.slice(0, plan.limit) : pages;
5565
5465
  }
5566
5466
  function generatedFeedInput(manifest, plan) {
@@ -5571,64 +5471,182 @@ function generatedFeedInput(manifest, plan) {
5571
5471
  homeUrl: canonicalUrl(manifest, home.path, home.group),
5572
5472
  pages: pages.map((page) => ({
5573
5473
  url: canonicalUrl(manifest, page.path, page.group),
5574
- title: page.title,
5575
- publishedAt: page.publishedAt,
5576
- updatedAt: page.updatedAt,
5577
- description: page.description,
5474
+ title: page.frontmatter.title,
5475
+ publishedAt: authoredDate(page.frontmatter.publishedAt),
5476
+ updatedAt: authoredDate(page.frontmatter.updatedAt),
5477
+ description: page.frontmatter.description,
5578
5478
  body: page.body,
5579
- tags: page.tags
5479
+ tags: Array.isArray(page.frontmatter.tags) ? page.frontmatter.tags.filter(
5480
+ (tag) => typeof tag == "string"
5481
+ ) : []
5580
5482
  }))
5581
5483
  };
5582
5484
  }
5583
5485
  function feedBody(manifest, plan) {
5584
5486
  if (plan.kind === "rss")
5585
5487
  return serializeRssFeed(generatedFeedInput(manifest, plan));
5586
- if (plan.kind === "atom")
5587
- return serializeAtomFeed(generatedFeedInput(manifest, plan));
5588
5488
  if (plan.kind === "json-feed")
5589
5489
  return serializeJsonFeed(generatedFeedInput(manifest, plan));
5590
5490
  }
5591
- function llmsFullTextBody(manifest, plan, outputPlans, artifacts) {
5592
- let home = outputHome(manifest, plan), pages = new Set(
5593
- discoveryPages({
5594
- manifest,
5595
- ...plan,
5596
- group: plan.scopeGroup ?? plan.group
5597
- }).map((page) => page.path)
5598
- ), visibleArtifacts = artifacts.filter(
5599
- (artifact) => pages.has(artifact.htmlPath)
5600
- ), index = outputPlans.find(
5601
- (candidate) => candidate.kind === "llms" && candidate.group === plan.group
5602
- ), sitemap = outputPlans.find(
5603
- (candidate) => candidate.kind === "sitemap-md" && candidate.group === plan.group
5604
- );
5491
+ function llmsFullTextBody({
5492
+ manifest,
5493
+ plan,
5494
+ artifactsByPath,
5495
+ markdownPaths,
5496
+ pagesByPath,
5497
+ indexPlan,
5498
+ sitemapPlan
5499
+ }) {
5500
+ let home = outputHome(manifest, plan), visibleArtifacts = navigationDiscoverySections({
5501
+ manifest,
5502
+ group: plan.scopeGroup ?? plan.group,
5503
+ scope: plan.scope,
5504
+ limit: plan.limit
5505
+ }).flatMap(
5506
+ (section) => section.entries.flatMap(
5507
+ (entry) => entry.type === "page" ? [entry.path] : []
5508
+ )
5509
+ ).flatMap((path) => {
5510
+ let artifact = artifactsByPath.get(path);
5511
+ if (!artifact) return [];
5512
+ let page = pagesByPath.get(path);
5513
+ return [
5514
+ {
5515
+ body: artifact.body,
5516
+ markdownPath: artifact.markdownPath,
5517
+ pageUrl: canonicalUrl(
5518
+ manifest,
5519
+ markdownPaths.has(artifact.markdownPath) ? artifact.markdownPath : artifact.htmlPath,
5520
+ page?.group
5521
+ ),
5522
+ title: artifact.title
5523
+ }
5524
+ ];
5525
+ });
5605
5526
  return serializeLlmsFull({
5606
- title: home.title,
5607
- indexUrl: index ? canonicalUrl(manifest, index.path, index.group) : void 0,
5608
- sitemapUrl: sitemap ? canonicalUrl(manifest, sitemap.path, sitemap.group) : void 0,
5527
+ title: home.title === manifest.site.name ? manifest.site.name : `${home.title} \u2014 ${manifest.site.name}`,
5528
+ indexUrl: indexPlan ? canonicalUrl(manifest, indexPlan.path, indexPlan.group) : void 0,
5529
+ sitemapUrl: sitemapPlan ? canonicalUrl(manifest, sitemapPlan.path, sitemapPlan.group) : void 0,
5609
5530
  artifacts: visibleArtifacts,
5610
- maxBytes: DEFAULT_LLMS_FULL_MAX_BYTES
5531
+ maxBytes: 52428800
5611
5532
  });
5612
5533
  }
5613
- function agentTextOutputsModule(manifest, outputPlans, artifacts = [], availableMarkdownPaths) {
5534
+ function llmsFullPartPath(path, partIndex) {
5535
+ return llmsPartPath(path, `part-${String(partIndex + 1).padStart(3, "0")}`);
5536
+ }
5537
+ function expandLlmsFullOutputPlans(options) {
5538
+ let outputPlans = [], bodies = /* @__PURE__ */ new Map(), pagesByPath = new Map(
5539
+ options.manifest.pages.map((page) => [page.path, page])
5540
+ ), artifactsByPath = new Map(
5541
+ options.artifacts.map((artifact) => [artifact.htmlPath, artifact])
5542
+ ), indexesByGroup = /* @__PURE__ */ new Map(), sitemapsByGroup = /* @__PURE__ */ new Map();
5543
+ for (let plan of options.outputPlans)
5544
+ plan.kind === "llms" && !indexesByGroup.has(plan.group) && indexesByGroup.set(plan.group, plan), plan.kind === "sitemap-md" && !sitemapsByGroup.has(plan.group) && sitemapsByGroup.set(plan.group, plan);
5545
+ for (let plan of options.outputPlans) {
5546
+ if (plan.kind !== "llms-full") {
5547
+ outputPlans.push(plan);
5548
+ continue;
5549
+ }
5550
+ let output = llmsFullTextBody({
5551
+ manifest: options.manifest,
5552
+ plan,
5553
+ artifactsByPath,
5554
+ markdownPaths: options.markdownPaths,
5555
+ pagesByPath,
5556
+ indexPlan: indexesByGroup.get(plan.group),
5557
+ sitemapPlan: sitemapsByGroup.get(plan.group)
5558
+ });
5559
+ for (let page of output.oversized)
5560
+ options.onWarning?.({
5561
+ code: "LLMS_FULL_PAGE_OVERSIZED",
5562
+ file: page.markdownPath,
5563
+ message: `The page body exceeds the 50 MB llms-full.txt limit. It is linked directly from the generated full-content output for ${plan.path}.`
5564
+ });
5565
+ if (output.parts.length > 1) {
5566
+ let title = outputHome(options.manifest, plan).title, fullTitle = title === options.manifest.site.name ? title : `${title} \u2014 ${options.manifest.site.name}`, gateway = serializeLlmsIndex({
5567
+ title: `${fullTitle} Full Content`,
5568
+ summary: `Full content is split into ${output.parts.length} ordered parts.`,
5569
+ sections: [
5570
+ {
5571
+ title: "Parts",
5572
+ entries: output.parts.map((_, partIndex) => {
5573
+ let path = llmsFullPartPath(plan.path, partIndex);
5574
+ return {
5575
+ title: `Part ${partIndex + 1}`,
5576
+ url: canonicalUrl(options.manifest, path, plan.group)
5577
+ };
5578
+ })
5579
+ }
5580
+ ]
5581
+ });
5582
+ if (output.parts.length > LLMS_INDEX_MAX_LINKS || new TextEncoder().encode(gateway).byteLength > 52428800)
5583
+ throw new RangeError(
5584
+ `LLMS full-content gateway "${plan.path}" exceeds ${LLMS_INDEX_MAX_LINKS} links or ${52428800} UTF-8 bytes.`
5585
+ );
5586
+ outputPlans.push(plan), bodies.set(plan.path, gateway), output.parts.forEach((body, partIndex) => {
5587
+ let path = llmsFullPartPath(plan.path, partIndex);
5588
+ outputPlans.push({ ...plan, path }), bodies.set(path, body);
5589
+ });
5590
+ } else
5591
+ outputPlans.push(plan), bodies.set(plan.path, output.parts[0] ?? "");
5592
+ }
5593
+ return { outputPlans, bodies };
5594
+ }
5595
+ function agentTextOutputsModule(manifest, outputPlans, artifacts = [], availableMarkdownPaths, fullContentBodies = /* @__PURE__ */ new Map(), indexBodies = /* @__PURE__ */ new Map()) {
5614
5596
  let markdownPaths = new Map(
5615
5597
  artifacts.filter(
5616
5598
  (artifact) => !availableMarkdownPaths || availableMarkdownPaths.has(artifact.markdownPath)
5617
5599
  ).map((artifact) => [artifact.htmlPath, artifact.markdownPath])
5618
- ), sitemap = outputPlans.find(
5619
- (output) => output.kind === "sitemap" && output.sitemaps !== void 0 && !output.group
5620
- ) ?? outputPlans.find((output) => output.kind === "sitemap" && !output.group), robotsSitemaps = outputPlans.filter(
5621
- (output) => output.kind === "sitemap" && effectiveOrigin(manifest, output.group) === manifest.site.origin && (output === sitemap || !sitemap || (sitemap.sitemaps ? !sitemap.sitemaps.some(
5622
- (entry) => entry.path === output.path && entry.group === output.group
5623
- ) : !output.path.startsWith(outputDirectory2(sitemap.path))))
5600
+ ), sitemapPlansByOrigin = /* @__PURE__ */ new Map();
5601
+ for (let output of outputPlans) {
5602
+ if (output.kind !== "sitemap") continue;
5603
+ let origin = effectiveOrigin(manifest, output.group), sitemaps = sitemapPlansByOrigin.get(origin) ?? [];
5604
+ sitemaps.push(output), sitemapPlansByOrigin.set(origin, sitemaps);
5605
+ }
5606
+ let robotsBody = (origin) => {
5607
+ let sitemaps = sitemapPlansByOrigin.get(origin) ?? [], rootSitemap = sitemaps.find(
5608
+ (output) => output.sitemaps !== void 0 && (output.group === void 0 || manifest.groups[output.group]?.path === "/" && effectiveOrigin(manifest, output.group) === origin)
5609
+ ), groupIndexes = sitemaps.filter(
5610
+ (candidate) => candidate.group && candidate.sitemaps !== void 0
5611
+ ), groupIndexGroups = new Set(
5612
+ groupIndexes.map((candidate) => candidate.group).filter((group) => !!group)
5613
+ ), referencedSitemaps = new Set(
5614
+ groupIndexes.flatMap(
5615
+ (candidate) => (candidate.sitemaps ?? []).map(
5616
+ (entry) => JSON.stringify([entry.path, entry.group ?? null])
5617
+ )
5618
+ )
5619
+ ), rootReferences = new Set(
5620
+ (rootSitemap?.sitemaps ?? []).map(
5621
+ (entry) => JSON.stringify([entry.path, entry.group ?? null])
5622
+ )
5623
+ );
5624
+ return `User-agent: *
5625
+ Allow: /
5626
+ ${sitemaps.filter((output) => output === rootSitemap || output.advertiseWithRobots === !0 ? !0 : rootSitemap ? rootSitemap.sitemaps !== void 0 ? output.sitemaps !== void 0 ? !1 : !rootReferences.has(
5627
+ JSON.stringify([output.path, output.group ?? null])
5628
+ ) : !output.path.startsWith(outputDirectory2(rootSitemap.path)) : output.sitemaps !== void 0 ? !(manifest.groups[output.group ?? ""]?.ancestry ?? []).some(
5629
+ (group) => group !== output.group && groupIndexGroups.has(group)
5630
+ ) : !referencedSitemaps.has(
5631
+ JSON.stringify([output.path, output.group ?? null])
5632
+ )).map((output) => `Sitemap: ${canonicalUrl(manifest, output.path, output.group)}
5633
+ `).join("")}`;
5634
+ }, robotsByOrigin = Object.fromEntries(
5635
+ [
5636
+ ...new Set(
5637
+ [
5638
+ manifest.site.origin,
5639
+ ...Object.keys(manifest.groups).map(
5640
+ (group) => effectiveOrigin(manifest, group)
5641
+ )
5642
+ ].filter((origin) => !!origin)
5643
+ )
5644
+ ].map((origin) => [origin, { body: robotsBody(origin) }])
5624
5645
  ), outputs = Object.fromEntries(
5625
5646
  outputPlans.filter(
5626
- (plan) => plan.kind === "robots" || plan.kind === "llms" || plan.kind === "llms-full" || plan.kind === "rss" || plan.kind === "atom" || plan.kind === "json-feed"
5647
+ (plan) => plan.kind === "robots" || plan.kind === "llms" || plan.kind === "llms-full" || plan.kind === "rss" || plan.kind === "json-feed"
5627
5648
  ).map((plan) => {
5628
- let body = plan.kind === "robots" ? `User-agent: *
5629
- Allow: /
5630
- ${robotsSitemaps.map((output) => `Sitemap: ${canonicalUrl(manifest, output.path, output.group)}
5631
- `).join("")}` : plan.kind === "llms-full" ? llmsFullTextBody(manifest, plan, outputPlans, artifacts) : feedBody(manifest, plan) ?? llmsTextBody(manifest, plan, markdownPaths);
5649
+ let body = plan.kind === "robots" ? robotsBody(manifest.site.origin) : plan.kind === "llms-full" ? fullContentBodies.get(plan.path) ?? "" : plan.kind === "llms" ? indexBodies.get(plan.path) ?? llmsTextBody(manifest, plan, markdownPaths).body : feedBody(manifest, plan) ?? "";
5632
5650
  return [
5633
5651
  plan.path,
5634
5652
  {
@@ -5641,6 +5659,7 @@ ${robotsSitemaps.map((output) => `Sitemap: ${canonicalUrl(manifest, output.path,
5641
5659
  })
5642
5660
  );
5643
5661
  return `export const agentTextOutputs = ${JSON.stringify(outputs, null, 2)} as const;
5662
+ export const agentTextOutputsByOrigin = ${JSON.stringify(robotsByOrigin, null, 2)} as const;
5644
5663
  `;
5645
5664
  }
5646
5665
  function agentRouteConfig(config, outputPlans, manifest, additionalPublicPaths, notFoundScopes, markdownPaths) {
@@ -5652,7 +5671,6 @@ function agentRouteConfig(config, outputPlans, manifest, additionalPublicPaths,
5652
5671
  enabled: config.agent?.enabled ?? !0,
5653
5672
  markdownAccept: config.agent?.markdownAccept ?? !0,
5654
5673
  markdownRoutes: config.agent?.markdownRoutes ?? !0,
5655
- plainTextAccept: config.agent?.plainTextAccept ?? !0,
5656
5674
  notFoundScopes,
5657
5675
  markdownPaths,
5658
5676
  outputPaths: outputPlans.map((output) => output.path),
@@ -5710,68 +5728,68 @@ function pageDiscoveryPlan({
5710
5728
  (a, b) => outputDirectory2(b.path).length - outputDirectory2(a.path).length || kinds.indexOf(a.kind) - kinds.indexOf(b.kind) || +(b.sitemaps !== void 0) - +(a.sitemaps !== void 0)
5711
5729
  )[0];
5712
5730
  }
5713
- function markdownLinkForPage(manifest, page) {
5714
- return `[${page.title}](${canonicalUrl(
5731
+ function markdownLinkForSequenceEntry(manifest, entry) {
5732
+ return `[${entry.title}](${canonicalUrl(
5715
5733
  manifest,
5716
- markdownRoutePathForPage(page.path),
5717
- page.group
5734
+ entry.pageKey ? markdownRoutePathForPage(entry.path) : entry.path,
5735
+ entry.group
5718
5736
  )})`;
5719
5737
  }
5720
- function collectPageNodes(nodes) {
5738
+ function collectSequenceNodes(nodes) {
5721
5739
  return nodes.flatMap((node) => node.type === "page" ? [
5722
5740
  {
5723
- fileID: node.fileID,
5724
- group: node.group,
5725
- path: node.path,
5726
- title: node.title
5741
+ pageKey: `${node.page.group}/${node.page.fileID}`,
5742
+ group: node.page.group,
5743
+ path: node.page.path,
5744
+ title: node.label || node.page.frontmatter.title
5727
5745
  }
5728
- ] : "children" in node ? collectPageNodes(node.children) : []);
5746
+ ] : node.type === "group" ? [
5747
+ ...node.presentation === "page" ? [
5748
+ {
5749
+ group: node.groupKey,
5750
+ path: node.route,
5751
+ title: node.label
5752
+ }
5753
+ ] : [],
5754
+ ...collectSequenceNodes(node.children)
5755
+ ] : node.type === "section" || node.type === "folder" ? collectSequenceNodes(node.children) : node.type === "view" ? collectSequenceNodes(node.items) : []);
5729
5756
  }
5730
5757
  function markdownPageKey(page) {
5731
5758
  return `${page.group}/${page.fileID}`;
5732
5759
  }
5733
- function pageReferenceKeys(page) {
5734
- return /* @__PURE__ */ new Set([
5735
- page.fileID,
5736
- markdownPageKey(page),
5737
- page.path,
5738
- markdownRoutePathForPage(page.path),
5739
- page.slug
5740
- ]);
5741
- }
5742
- function createMarkdownEnrichmentContext(manifest) {
5760
+ function createMarkdownEnrichmentContext(manifest, includeDrafts) {
5743
5761
  let publishedPages = manifest.pages.filter(
5744
- (page) => page.status === "published"
5762
+ (page) => page.frontmatter.status === "published"
5745
5763
  ), pagesByKey = new Map(
5746
5764
  manifest.pages.map((page) => [markdownPageKey(page), page])
5747
- ), groupPages = /* @__PURE__ */ new Map(), pagePositions = /* @__PURE__ */ new Map();
5748
- for (let [groupKey, group] of Object.entries(manifest.groups)) {
5749
- let pages = collectPageNodes(group.tree).flatMap((node) => {
5750
- if (node.group !== groupKey) return [];
5751
- let page = pagesByKey.get(`${node.group}/${node.fileID}`);
5752
- return page?.status === "published" ? [page] : [];
5753
- });
5754
- groupPages.set(groupKey, pages), pages.forEach((page, index) => {
5755
- let key = markdownPageKey(page);
5756
- pagePositions.has(key) || pagePositions.set(key, index);
5765
+ ), groupPages = /* @__PURE__ */ new Map(), pagePositions = /* @__PURE__ */ new Map(), rootGroups = new Set(
5766
+ Object.values(manifest.groups).map((group) => group.navigation.rootGroup)
5767
+ );
5768
+ for (let rootGroup of rootGroups) {
5769
+ let navigation = manifest.groups[rootGroup]?.navigation, sequence = collectSequenceNodes(navigation?.items ?? []).flatMap(
5770
+ (entry) => {
5771
+ if (!entry.pageKey) return [entry];
5772
+ let page = pagesByKey.get(entry.pageKey);
5773
+ return page && (includeDrafts || page.frontmatter.status === "published") ? [
5774
+ {
5775
+ ...entry,
5776
+ group: page.group,
5777
+ path: page.path,
5778
+ pageKey: markdownPageKey(page)
5779
+ }
5780
+ ] : [];
5781
+ }
5782
+ );
5783
+ for (let group of Object.values(manifest.groups))
5784
+ group.navigation.rootGroup === rootGroup && groupPages.set(group.key, sequence);
5785
+ sequence.forEach((entry, index) => {
5786
+ entry.pageKey && !pagePositions.has(entry.pageKey) && pagePositions.set(entry.pageKey, index);
5757
5787
  });
5758
5788
  }
5759
- let pagesByReference = /* @__PURE__ */ new Map(), pagesByTag = /* @__PURE__ */ new Map(), append = (index, key, page) => {
5760
- let pages = index.get(key);
5761
- pages ? pages.push(page) : index.set(key, [page]);
5762
- };
5763
- return publishedPages.forEach((page) => {
5764
- for (let key of pageReferenceKeys(page))
5765
- append(pagesByReference, key, page);
5766
- for (let tag of new Set(page.tags))
5767
- append(pagesByTag, tag, page);
5768
- }), {
5789
+ return {
5769
5790
  publishedPages,
5770
5791
  groupPages,
5771
- pagePositions,
5772
- pagesByReference,
5773
- pagesByTag,
5774
- sourceOrder: new Map(publishedPages.map((page, index) => [page, index]))
5792
+ pagePositions
5775
5793
  };
5776
5794
  }
5777
5795
  function adjacentMarkdownPages(options) {
@@ -5780,52 +5798,6 @@ function adjacentMarkdownPages(options) {
5780
5798
  );
5781
5799
  return !pages || index === void 0 ? {} : { previous: pages[index - 1], next: pages[index + 1] };
5782
5800
  }
5783
- function relatedFrontmatterReferences(page) {
5784
- let related = page.meta.related ?? page.meta.relatedContent;
5785
- return typeof related == "string" ? [related] : Array.isArray(related) ? related.filter(
5786
- (entry) => typeof entry == "string"
5787
- ) : [];
5788
- }
5789
- function lastOtherPage(pages, currentKey) {
5790
- if (pages)
5791
- for (let index = pages.length - 1; index >= 0; index--) {
5792
- let page = pages[index];
5793
- if (page && markdownPageKey(page) !== currentKey) return page;
5794
- }
5795
- }
5796
- function relatedMarkdownPages(options) {
5797
- let maxItems = options.maxItems ?? 5, currentKey = markdownPageKey(options.page), related = [], seen = /* @__PURE__ */ new Set();
5798
- for (let reference of relatedFrontmatterReferences(options.page)) {
5799
- let page = lastOtherPage(
5800
- options.context.pagesByReference.get(reference),
5801
- currentKey
5802
- );
5803
- if (!page) continue;
5804
- let key = markdownPageKey(page);
5805
- seen.has(key) || (seen.add(key), related.push(page));
5806
- }
5807
- let currentTags = new Set(options.page.tags);
5808
- if (currentTags.size > 0) {
5809
- let candidates = /* @__PURE__ */ new Set();
5810
- for (let tag of currentTags)
5811
- for (let page of options.context.pagesByTag.get(tag) ?? [])
5812
- candidates.add(page);
5813
- let tagged = [...candidates].filter(
5814
- (candidate) => markdownPageKey(candidate) !== currentKey && !seen.has(markdownPageKey(candidate))
5815
- ).map((candidate) => ({
5816
- page: candidate,
5817
- score: candidate.tags.filter((tag) => currentTags.has(tag)).length
5818
- })).filter((candidate) => candidate.score > 0).sort(
5819
- (a, b) => b.score - a.score || a.page.group.localeCompare(b.page.group) || a.page.path.localeCompare(b.page.path) || (options.context.sourceOrder.get(a.page) ?? 0) - (options.context.sourceOrder.get(b.page) ?? 0)
5820
- );
5821
- for (let candidate of tagged) {
5822
- let key = markdownPageKey(candidate.page);
5823
- if (!seen.has(key) && (seen.add(key), related.push(candidate.page), related.length >= maxItems))
5824
- break;
5825
- }
5826
- }
5827
- return related.slice(0, maxItems);
5828
- }
5829
5801
  function markdownListSection(title, items) {
5830
5802
  if (items.length !== 0)
5831
5803
  return [`## ${title}`, ...items.map((item) => `- ${item}`)].join(`
@@ -5838,23 +5810,16 @@ function markdownArtifactEnrichment(options) {
5838
5810
  }), group = options.manifest.groups[options.page.group], llms = pageDiscoveryPlan({ ...options, kinds: ["llms"] }), sitemap = pageDiscoveryPlan({
5839
5811
  ...options,
5840
5812
  kinds: ["sitemap-md", "sitemap"]
5841
- }), related = relatedMarkdownPages({
5842
- context: options.context,
5843
- page: options.page
5844
5813
  }), continueItems = [
5845
- previous ? `Previous: ${markdownLinkForPage(options.manifest, previous)}` : void 0,
5846
- next ? `Next: ${markdownLinkForPage(options.manifest, next)}` : void 0
5847
- ].filter((item) => !!item), relatedItems = related.map((page) => {
5848
- let description = page.description ? ` \u2014 ${page.description}` : "";
5849
- return `${markdownLinkForPage(options.manifest, page)}${description}`;
5850
- }), sectionItems = [
5814
+ previous ? `Previous: ${markdownLinkForSequenceEntry(options.manifest, previous)}` : void 0,
5815
+ next ? `Next: ${markdownLinkForSequenceEntry(options.manifest, next)}` : void 0
5816
+ ].filter((item) => !!item), sectionItems = [
5851
5817
  group?.path ? `[${group.title} home](${canonicalUrl(options.manifest, group.path, options.page.group)})` : void 0,
5852
5818
  llms ? `[LLMS index](${canonicalUrl(options.manifest, llms.path, llms.group)})` : void 0,
5853
5819
  sitemap ? `[Sitemap](${canonicalUrl(options.manifest, sitemap.path, sitemap.group)})` : void 0
5854
5820
  ].filter((item) => !!item);
5855
5821
  return [
5856
5822
  markdownListSection("Continue reading", continueItems),
5857
- markdownListSection("Related content", relatedItems),
5858
5823
  markdownListSection("Section links", sectionItems)
5859
5824
  ].filter((section) => !!section);
5860
5825
  }
@@ -5864,17 +5829,17 @@ function markdownArtifactBody(options) {
5864
5829
  kinds: ["sitemap-md", "sitemap"]
5865
5830
  }), frontmatter = [
5866
5831
  "---",
5867
- `title: ${yamlString(options.page.title)}`,
5868
- options.page.description ? `description: ${yamlString(options.page.description)}` : void 0,
5832
+ `title: ${yamlString(options.page.frontmatter.title)}`,
5833
+ options.page.frontmatter.description ? `description: ${yamlString(options.page.frontmatter.description)}` : void 0,
5869
5834
  `url: ${yamlString(canonicalUrl(options.manifest, markdownPath, options.page.group))}`,
5870
5835
  group?.title ? `section: ${yamlString(group.title)}` : void 0,
5871
- options.page.updatedAt ? `updated: ${yamlString(options.page.updatedAt)}` : void 0,
5836
+ authoredDate(options.page.frontmatter.updatedAt) ? `updated: ${yamlString(authoredDate(options.page.frontmatter.updatedAt))}` : void 0,
5872
5837
  "---"
5873
5838
  ].filter(Boolean), context = [
5874
5839
  `> Breadcrumbs: ${groupBreadcrumbs(
5875
5840
  options.manifest,
5876
5841
  options.page.group,
5877
- options.page.title
5842
+ options.page.frontmatter.title
5878
5843
  )}`,
5879
5844
  sitemap ? `> Sitemap: ${canonicalUrl(options.manifest, sitemap.path, sitemap.group)}` : void 0,
5880
5845
  llms ? `> LLMS index: ${canonicalUrl(options.manifest, llms.path, llms.group)}` : void 0
@@ -5890,7 +5855,7 @@ function markdownArtifactBody(options) {
5890
5855
  `),
5891
5856
  context.join(`
5892
5857
  `),
5893
- markdownHeading(options.page.title),
5858
+ markdownHeading(options.page.frontmatter.title),
5894
5859
  body,
5895
5860
  ...enrichment,
5896
5861
  ""
@@ -5898,9 +5863,12 @@ function markdownArtifactBody(options) {
5898
5863
 
5899
5864
  `);
5900
5865
  }
5901
- function markdownArtifacts(manifest, outputPlans, conversions, rootOwnedGroups) {
5902
- let enrichmentContext = createMarkdownEnrichmentContext(manifest);
5903
- return enrichmentContext.publishedPages.map((page) => {
5866
+ function markdownArtifacts(manifest, outputPlans, conversions, rootOwnedGroups, includeDrafts) {
5867
+ let enrichmentContext = createMarkdownEnrichmentContext(
5868
+ manifest,
5869
+ includeDrafts
5870
+ );
5871
+ return (includeDrafts ? manifest.pages : enrichmentContext.publishedPages).map((page) => {
5904
5872
  let body = markdownArtifactBody({
5905
5873
  manifest,
5906
5874
  outputPlans,
@@ -5915,8 +5883,9 @@ function markdownArtifacts(manifest, outputPlans, conversions, rootOwnedGroups)
5915
5883
  body,
5916
5884
  htmlPath: page.path,
5917
5885
  markdownPath,
5886
+ ...page.frontmatter.metadata.robots ? { robots: page.frontmatter.metadata.robots } : {},
5918
5887
  sourceHash: markdownContentHash(page.body),
5919
- title: page.title
5888
+ title: page.frontmatter.title
5920
5889
  };
5921
5890
  });
5922
5891
  }
@@ -5933,6 +5902,7 @@ function createMarkdownArtifactShards(artifacts) {
5933
5902
  artifact.markdownPath,
5934
5903
  artifact.bodyHash,
5935
5904
  artifact.sourceHash,
5905
+ JSON.stringify(artifact.robots ?? {}),
5936
5906
  artifact.byteSize
5937
5907
  ].join(":")
5938
5908
  ).join(`
@@ -5952,6 +5922,7 @@ function artifactIndexEntry(artifact, shardId) {
5952
5922
  byteSize: artifact.byteSize,
5953
5923
  htmlPath: artifact.htmlPath,
5954
5924
  markdownPath: artifact.markdownPath,
5925
+ ...artifact.robots ? { robots: artifact.robots } : {},
5955
5926
  shardId,
5956
5927
  sourceHash: artifact.sourceHash,
5957
5928
  title: artifact.title
@@ -6067,13 +6038,15 @@ function markdownArtifactsReport(artifacts, shards) {
6067
6038
  modulePath: shard.modulePath,
6068
6039
  pageCount: shard.artifacts.length
6069
6040
  })),
6070
- totalBytes: byteSizes.reduce((total, bytes) => total + bytes, 0)
6041
+ totalBytes: byteSizes.reduce((total, bytes2) => total + bytes2, 0)
6071
6042
  };
6072
6043
  }
6073
6044
  function markdownDiscoveryLinks(outputPlans, manifest) {
6074
6045
  let llms = outputPlans.find(
6075
6046
  (plan) => plan.kind === "llms" && !plan.group
6076
- )?.path, sitemap = outputPlans.find((plan) => plan.kind === "sitemap-md")?.path, resolve8 = (path) => manifest ? canonicalUrl(manifest, path) : path;
6047
+ )?.path, sitemap = outputPlans.find(
6048
+ (plan) => plan.kind === "sitemap-md" && !plan.group
6049
+ )?.path, resolve8 = (path) => manifest ? canonicalUrl(manifest, path) : path;
6077
6050
  return {
6078
6051
  home: resolve8("/"),
6079
6052
  ...llms ? { llms: resolve8(llms) } : {},
@@ -6140,7 +6113,7 @@ async function generatedLeafPageModule({
6140
6113
  }
6141
6114
  function outputDefaultOptions(options) {
6142
6115
  let normalized = {};
6143
- return options?.scope && (normalized.scope = options.scope), options?.limit && (normalized.limit = options.limit), options?.status && (normalized.status = options.status), options?.includeDescriptions !== void 0 && (normalized.includeDescriptions = options.includeDescriptions), normalized;
6116
+ return options?.scope && (normalized.scope = options.scope), options?.limit && (normalized.limit = options.limit), options?.includeDescriptions !== void 0 && (normalized.includeDescriptions = options.includeDescriptions), normalized;
6144
6117
  }
6145
6118
  function mergeOutputDefaultOptions(base, override) {
6146
6119
  if (!(!base && !override))
@@ -6181,7 +6154,7 @@ function outputLimit(defaults, declaration) {
6181
6154
  return declaration.limit ?? defaults?.limit;
6182
6155
  }
6183
6156
  function planGeneratedOutputs(config, manifest) {
6184
- let plans = /* @__PURE__ */ new Map(), add = (plan) => {
6157
+ let plans = /* @__PURE__ */ new Map(), add2 = (plan) => {
6185
6158
  let normalizedPath = normalizeOutputPath(plan.path), origin = effectiveOrigin(manifest, plan.group), key = JSON.stringify([origin, normalizedPath]), existing = plans.get(key);
6186
6159
  if (existing)
6187
6160
  throw new AppError({
@@ -6206,16 +6179,18 @@ function planGeneratedOutputs(config, manifest) {
6206
6179
  llms: config.outputs?.llms ?? rootMountedOutputs?.llms,
6207
6180
  llmsFull: config.outputs?.llmsFull ?? rootMountedOutputs?.llmsFull
6208
6181
  };
6209
- if (rootOutputs.robots !== !1 && add({ kind: "robots", path: rootOutputs.robots?.path ?? "/robots.txt" }), rootOutputs.sitemap !== !1 && add({
6182
+ if (rootOutputs.robots !== !1 && add2({ kind: "robots", path: rootOutputs.robots?.path ?? "/robots.txt" }), rootOutputs.sitemap !== !1 && add2({
6210
6183
  kind: "sitemap",
6211
6184
  path: rootOutputs.sitemap?.path ?? "/sitemap.xml",
6185
+ ...rootOutputs.sitemap?.mode === "urls" ? { allUrls: !0 } : {},
6212
6186
  ...rootOutputs.sitemap?.mode === "urls" ? {} : { sitemaps: [] }
6213
- }), rootOutputs.sitemapMarkdown !== !1 && add({
6187
+ }), rootOutputs.sitemapMarkdown !== !1 && add2({
6214
6188
  kind: "sitemap-md",
6215
6189
  path: rootOutputs.sitemapMarkdown?.path ?? "/sitemap.md",
6216
6190
  scopeGroup: rootScopeGroup,
6217
- scope: rootOutputs.sitemapMarkdown?.scope ?? (config.outputs?.sitemapMarkdown === void 0 && rootMountedOutputs?.sitemapMarkdown ? "group" : "site")
6218
- }), rootOutputs.llms !== !1 && add({
6191
+ scope: rootOutputs.sitemapMarkdown?.scope ?? (config.outputs?.sitemapMarkdown === void 0 && rootMountedOutputs?.sitemapMarkdown ? "group" : "site"),
6192
+ limit: rootOutputs.sitemapMarkdown?.limit
6193
+ }), rootOutputs.llms !== !1 && add2({
6219
6194
  kind: "llms",
6220
6195
  path: rootOutputs.llms?.path ?? "/llms.txt",
6221
6196
  scopeGroup: rootScopeGroup,
@@ -6226,7 +6201,7 @@ function planGeneratedOutputs(config, manifest) {
6226
6201
  ),
6227
6202
  limit: outputLimit(rootLlmsDefaults, rootOutputs.llms ?? {}),
6228
6203
  includeDescriptions: rootLlmsDefaults?.includeDescriptions
6229
- }), rootOutputs.llmsFull !== !1 && add({
6204
+ }), rootOutputs.llmsFull !== !1 && add2({
6230
6205
  kind: "llms-full",
6231
6206
  path: rootOutputs.llmsFull?.path ?? "/llms-full.txt",
6232
6207
  scopeGroup: rootScopeGroup,
@@ -6234,59 +6209,62 @@ function planGeneratedOutputs(config, manifest) {
6234
6209
  limit: rootOutputs.llmsFull?.limit
6235
6210
  }), rootOutputs.feeds?.rss) {
6236
6211
  let defaults = feedDefaults(rootDefaults, "rss");
6237
- add({
6212
+ add2({
6238
6213
  kind: "rss",
6239
6214
  path: rootOutputs.feeds.rss.path,
6240
6215
  scope: outputScope(defaults, rootOutputs.feeds.rss, "site"),
6241
- limit: outputLimit(defaults, rootOutputs.feeds.rss),
6242
- status: defaults?.status
6216
+ limit: outputLimit(defaults, rootOutputs.feeds.rss)
6243
6217
  });
6244
6218
  }
6245
6219
  if (rootOutputs.feeds?.atom) {
6246
6220
  let defaults = feedDefaults(rootDefaults, "atom");
6247
- add({
6221
+ add2({
6248
6222
  kind: "atom",
6249
6223
  path: rootOutputs.feeds.atom.path,
6250
6224
  scope: outputScope(defaults, rootOutputs.feeds.atom, "site"),
6251
- limit: outputLimit(defaults, rootOutputs.feeds.atom),
6252
- status: defaults?.status
6225
+ limit: outputLimit(defaults, rootOutputs.feeds.atom)
6253
6226
  });
6254
6227
  }
6255
6228
  if (rootOutputs.feeds?.json) {
6256
6229
  let defaults = feedDefaults(rootDefaults, "json");
6257
- add({
6230
+ add2({
6258
6231
  kind: "json-feed",
6259
6232
  path: rootOutputs.feeds.json.path,
6260
6233
  scope: outputScope(defaults, rootOutputs.feeds.json, "site"),
6261
- limit: outputLimit(defaults, rootOutputs.feeds.json),
6262
- status: defaults?.status
6234
+ limit: outputLimit(defaults, rootOutputs.feeds.json)
6263
6235
  });
6264
6236
  }
6265
- for (let group of Object.values(config.groups)) {
6237
+ let configuredGroups = Object.values(config.groups), groupsWithSitemapDescendants = /* @__PURE__ */ new Set();
6238
+ for (let candidate of configuredGroups)
6239
+ if (candidate.outputs?.sitemap !== !1)
6240
+ for (let separator = candidate.key.indexOf("/"); separator >= 0; separator = candidate.key.indexOf("/", separator + 1))
6241
+ groupsWithSitemapDescendants.add(candidate.key.slice(0, separator));
6242
+ let groupsWithPublishedUrls = new Set(
6243
+ manifest.pages.filter(
6244
+ (page) => page.frontmatter.status === "published" && page.frontmatter.metadata.robots?.index !== !1
6245
+ ).map((page) => page.group)
6246
+ );
6247
+ for (let group of configuredGroups) {
6266
6248
  let defaults = resolveOutputDefaults(config, group), nested = group.key !== ROOT_CONTENT_GROUP_KEY && group.path !== "/";
6267
6249
  if (group.key !== rootMountedGroup?.key && (group.outputs?.sitemap || nested && group.outputs?.sitemap !== !1)) {
6268
- if (group.outputs?.sitemap && group.outputs.sitemap.mode === "index")
6269
- throw new AppError({
6270
- code: "FRAMEWORK_OUTPUT_PATH_INVALID",
6271
- message: `Group "${group.key}" cannot declare a sitemap index.`,
6272
- fix: 'Use mode "urls" for a group sitemap, or configure the index in root outputs.sitemap.',
6273
- details: {
6274
- group: group.key,
6275
- propertyPath: `groups.${group.key}.outputs.sitemap.mode`
6276
- }
6277
- });
6278
- add({
6250
+ let sitemapPath = joinOutputPath(
6251
+ group.path,
6252
+ group.outputs?.sitemap ? group.outputs.sitemap.path ?? "sitemap.xml" : "sitemap.xml"
6253
+ ), hasEnabledDescendants = groupsWithSitemapDescendants.has(group.key), index = group.outputs?.sitemap?.mode === "index" || hasEnabledDescendants, ownsUrls = groupsWithPublishedUrls.has(group.key);
6254
+ index ? (add2({
6279
6255
  kind: "sitemap",
6280
- path: joinOutputPath(
6281
- group.path,
6282
- group.outputs?.sitemap ? group.outputs.sitemap.path ?? "sitemap.xml" : "sitemap.xml"
6283
- ),
6256
+ path: sitemapPath,
6257
+ group: group.key,
6258
+ sitemaps: []
6259
+ }), ownsUrls && add2({
6260
+ kind: "sitemap",
6261
+ path: `${sitemapPath.replace(/\.xml$/, "")}-pages.xml`,
6284
6262
  group: group.key
6285
- });
6263
+ })) : ownsUrls && add2({ kind: "sitemap", path: sitemapPath, group: group.key });
6286
6264
  }
6287
6265
  if (group.key !== rootMountedGroup?.key && (group.outputs?.llms || nested && group.outputs?.llms !== !1)) {
6288
6266
  let llmsDefaults = defaults.llms;
6289
- add({
6267
+ add2({
6290
6268
  kind: "llms",
6291
6269
  path: joinOutputPath(
6292
6270
  group.path,
@@ -6298,15 +6276,16 @@ function planGeneratedOutputs(config, manifest) {
6298
6276
  includeDescriptions: llmsDefaults?.includeDescriptions
6299
6277
  });
6300
6278
  }
6301
- if (group.key !== rootMountedGroup?.key && (group.outputs?.sitemapMarkdown || nested && group.outputs?.sitemapMarkdown !== !1) && add({
6279
+ if (group.key !== rootMountedGroup?.key && (group.outputs?.sitemapMarkdown || nested && group.outputs?.sitemapMarkdown !== !1) && add2({
6302
6280
  kind: "sitemap-md",
6303
6281
  path: joinOutputPath(
6304
6282
  group.path,
6305
6283
  group.outputs?.sitemapMarkdown ? group.outputs.sitemapMarkdown.path : "sitemap.md"
6306
6284
  ),
6307
6285
  group: group.key,
6308
- scope: group.outputs?.sitemapMarkdown ? group.outputs.sitemapMarkdown.scope ?? "group" : "group"
6309
- }), group.key !== rootMountedGroup?.key && group.outputs?.llmsFull && add({
6286
+ scope: group.outputs?.sitemapMarkdown ? group.outputs.sitemapMarkdown.scope ?? "group" : "group",
6287
+ limit: group.outputs?.sitemapMarkdown?.limit
6288
+ }), group.key !== rootMountedGroup?.key && group.outputs?.llmsFull && add2({
6310
6289
  kind: "llms-full",
6311
6290
  path: joinOutputPath(group.path, group.outputs.llmsFull.path),
6312
6291
  group: group.key,
@@ -6314,59 +6293,77 @@ function planGeneratedOutputs(config, manifest) {
6314
6293
  limit: group.outputs.llmsFull.limit
6315
6294
  }), group.outputs?.feeds?.rss) {
6316
6295
  let feedOptions = feedDefaults(defaults, "rss");
6317
- add({
6296
+ add2({
6318
6297
  kind: "rss",
6319
6298
  path: joinOutputPath(group.path, group.outputs.feeds.rss.path),
6320
6299
  group: group.key,
6321
6300
  scope: outputScope(feedOptions, group.outputs.feeds.rss, "group"),
6322
- limit: outputLimit(feedOptions, group.outputs.feeds.rss),
6323
- status: feedOptions?.status
6301
+ limit: outputLimit(feedOptions, group.outputs.feeds.rss)
6324
6302
  });
6325
6303
  }
6326
6304
  if (group.outputs?.feeds?.atom) {
6327
6305
  let feedOptions = feedDefaults(defaults, "atom");
6328
- add({
6306
+ add2({
6329
6307
  kind: "atom",
6330
6308
  path: joinOutputPath(group.path, group.outputs.feeds.atom.path),
6331
6309
  group: group.key,
6332
6310
  scope: outputScope(feedOptions, group.outputs.feeds.atom, "group"),
6333
- limit: outputLimit(feedOptions, group.outputs.feeds.atom),
6334
- status: feedOptions?.status
6311
+ limit: outputLimit(feedOptions, group.outputs.feeds.atom)
6335
6312
  });
6336
6313
  }
6337
6314
  if (group.outputs?.feeds?.json) {
6338
6315
  let feedOptions = feedDefaults(defaults, "json");
6339
- add({
6316
+ add2({
6340
6317
  kind: "json-feed",
6341
6318
  path: joinOutputPath(group.path, group.outputs.feeds.json.path),
6342
6319
  group: group.key,
6343
6320
  scope: outputScope(feedOptions, group.outputs.feeds.json, "group"),
6344
- limit: outputLimit(feedOptions, group.outputs.feeds.json),
6345
- status: feedOptions?.status
6321
+ limit: outputLimit(feedOptions, group.outputs.feeds.json)
6346
6322
  });
6347
6323
  }
6348
6324
  }
6349
- let xmlLeaves = [...plans.values()].filter(
6350
- (plan) => plan.kind === "sitemap" && plan.group
6351
- ), rootSitemap = [...plans.values()].find(
6325
+ let xmlPlans = [...plans.values()].filter(
6326
+ (plan) => plan.kind === "sitemap"
6327
+ ), xmlLeaves = xmlPlans.filter((plan) => plan.sitemaps === void 0), xmlLeavesByOriginDirectory = sitemapLeavesByOriginDirectory(
6328
+ manifest,
6329
+ xmlLeaves
6330
+ ), xmlLeavesByDirectory = /* @__PURE__ */ new Map(), childDirectoriesByOrigin = /* @__PURE__ */ new Map();
6331
+ for (let leaf of xmlLeaves) {
6332
+ let origin = effectiveOrigin(manifest, leaf.group), directory = outputDirectory2(leaf.path), directoryKey = JSON.stringify([origin ?? null, directory]), directoryLeaves = xmlLeavesByDirectory.get(directoryKey) ?? [];
6333
+ directoryLeaves.push(leaf), xmlLeavesByDirectory.set(directoryKey, directoryLeaves);
6334
+ let parentDirectory = parentOutputDirectory(directory);
6335
+ if (!parentDirectory) continue;
6336
+ let parentKey = JSON.stringify([origin ?? null, parentDirectory]), childDirectories = childDirectoriesByOrigin.get(parentKey) ?? /* @__PURE__ */ new Set();
6337
+ childDirectories.add(directory), childDirectoriesByOrigin.set(parentKey, childDirectories);
6338
+ }
6339
+ for (let index of xmlPlans.filter((plan) => plan.sitemaps !== void 0)) {
6340
+ let directory = outputDirectory2(index.path), key = JSON.stringify([
6341
+ effectiveOrigin(manifest, index.group) ?? null,
6342
+ directory
6343
+ ]);
6344
+ index.sitemaps = (xmlLeavesByOriginDirectory.get(key) ?? []).filter((leaf) => leaf.path !== index.path).map(({ path, group }) => ({ path, ...group ? { group } : {} })), index.group && !index.sitemaps.length && plans.delete(
6345
+ JSON.stringify([effectiveOrigin(manifest, index.group), index.path])
6346
+ );
6347
+ }
6348
+ let rootSitemap = [...plans.values()].find(
6352
6349
  (plan) => plan.kind === "sitemap" && !plan.group
6353
6350
  );
6354
6351
  if (rootSitemap?.sitemaps) {
6355
- let directory = outputDirectory2(rootSitemap.path), leaves = xmlLeaves.filter(
6356
- (plan) => effectiveOrigin(manifest, plan.group) === manifest.site.origin && plan.path.startsWith(directory)
6357
- );
6358
- if ([
6352
+ let directory = outputDirectory2(rootSitemap.path), rootLeafKey = JSON.stringify([manifest.site.origin, directory]), leaves = [...xmlLeavesByOriginDirectory.get(rootLeafKey) ?? []], rootPaths = [
6359
6353
  "/",
6360
6354
  ...Object.values(manifest.groups).filter(
6361
6355
  (group) => effectiveOrigin(manifest, group.key) === manifest.site.origin
6362
6356
  ).map((group) => group.path),
6363
6357
  ...manifest.pages.filter(
6364
- (page) => page.status === "published" && effectiveOrigin(manifest, page.group) === manifest.site.origin
6358
+ (page) => page.frontmatter.status === "published" && page.frontmatter.metadata.robots?.index !== !1 && effectiveOrigin(manifest, page.group) === manifest.site.origin
6365
6359
  ).map((page) => page.path)
6366
- ].some(
6367
- (path) => path.startsWith(directory) && !leaves.some((leaf) => path.startsWith(outputDirectory2(leaf.path)))
6360
+ ], leafDirectories = new Set(
6361
+ leaves.map((leaf) => outputDirectory2(leaf.path))
6362
+ );
6363
+ if (rootPaths.some(
6364
+ (path) => path.startsWith(directory) && !isWithinOutputDirectory(path, leafDirectories)
6368
6365
  )) {
6369
- let rootLeaf = add({
6366
+ let rootLeaf = add2({
6370
6367
  kind: "sitemap",
6371
6368
  path: `${rootSitemap.path.replace(/\.[^/.]+$/, "")}-pages.xml`
6372
6369
  });
@@ -6385,10 +6382,16 @@ function planGeneratedOutputs(config, manifest) {
6385
6382
  xmlLeaves.push(...leaves.filter((leaf) => !leaf.group));
6386
6383
  }
6387
6384
  for (let leaf of xmlLeaves) {
6388
- let directory = outputDirectory2(leaf.path);
6389
- leaf.exclude = xmlLeaves.filter(
6390
- (candidate) => candidate !== leaf && effectiveOrigin(manifest, candidate.group) === effectiveOrigin(manifest, leaf.group) && outputDirectory2(candidate.path).startsWith(directory) && outputDirectory2(candidate.path).length > directory.length
6391
- ).map(({ path, group }) => ({ path, ...group ? { group } : {} }));
6385
+ if (leaf.allUrls) continue;
6386
+ let directory = outputDirectory2(leaf.path), origin = effectiveOrigin(manifest, leaf.group), parentKey = JSON.stringify([origin ?? null, directory]), childDirectories = childDirectoriesByOrigin.get(parentKey) ?? [];
6387
+ leaf.exclude = [...childDirectories].flatMap(
6388
+ (childDirectory) => (xmlLeavesByDirectory.get(
6389
+ JSON.stringify([origin ?? null, childDirectory])
6390
+ ) ?? []).map(({ path, group }) => ({
6391
+ path,
6392
+ ...group ? { group } : {}
6393
+ }))
6394
+ );
6392
6395
  }
6393
6396
  let groupLlmsPlans = [...plans.values()].filter(
6394
6397
  (plan) => plan.kind === "llms" && plan.group
@@ -6434,9 +6437,41 @@ function planGeneratedOutputs(config, manifest) {
6434
6437
  path: candidate.path,
6435
6438
  group: candidate.group
6436
6439
  }))));
6440
+ let sitemapMarkdownPlans = [...plans.values()].filter(
6441
+ (plan) => plan.kind === "sitemap-md" && plan.group
6442
+ ), sitemapMarkdownPlansByGroup = new Map(
6443
+ sitemapMarkdownPlans.map((plan) => [plan.group, plan])
6444
+ ), sitemapMarkdownDescendantsByGroup = /* @__PURE__ */ new Map(), sitemapMarkdownChildrenByGroup = /* @__PURE__ */ new Map(), rootSitemapMarkdownPlans = [];
6445
+ for (let candidate of sitemapMarkdownPlans) {
6446
+ let group = candidate.group;
6447
+ if (!group) continue;
6448
+ let ancestry = manifest.groups[group]?.ancestry ?? [];
6449
+ ancestry.length === 1 && ancestry[0] === group && rootSitemapMarkdownPlans.push(candidate);
6450
+ for (let ancestor of ancestry) {
6451
+ if (ancestor === group) continue;
6452
+ let descendants = sitemapMarkdownDescendantsByGroup.get(ancestor) ?? [];
6453
+ descendants.push(candidate), sitemapMarkdownDescendantsByGroup.set(ancestor, descendants);
6454
+ }
6455
+ for (let index = ancestry.length - 2; index >= 0; index -= 1) {
6456
+ let ancestor = ancestry[index];
6457
+ if (!ancestor || !sitemapMarkdownPlansByGroup.has(ancestor)) continue;
6458
+ let children = sitemapMarkdownChildrenByGroup.get(ancestor) ?? [];
6459
+ children.push(candidate), sitemapMarkdownChildrenByGroup.set(ancestor, children);
6460
+ break;
6461
+ }
6462
+ }
6463
+ for (let plan of plans.values()) {
6464
+ if (plan.kind !== "sitemap-md") continue;
6465
+ let baseGroup = plan.group ?? plan.scopeGroup ?? ROOT_CONTENT_GROUP_KEY, descendants = baseGroup === ROOT_CONTENT_GROUP_KEY ? rootSitemapMarkdownPlans : sitemapMarkdownDescendantsByGroup.get(baseGroup) ?? [], directDescendants = baseGroup === ROOT_CONTENT_GROUP_KEY ? rootSitemapMarkdownPlans : sitemapMarkdownChildrenByGroup.get(baseGroup) ?? [];
6466
+ plan.groupSitemaps = directDescendants.map((candidate) => ({
6467
+ title: config.groups[candidate.group ?? ""]?.title ?? candidate.group ?? "Group",
6468
+ path: candidate.path,
6469
+ group: candidate.group
6470
+ })), plan.excludeGroups = descendants.map((candidate) => candidate.group).filter((group) => !!group);
6471
+ }
6437
6472
  return [...plans.values()].sort((a, b) => a.path.localeCompare(b.path));
6438
6473
  }
6439
- function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest) {
6474
+ function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest, content) {
6440
6475
  let outputKey = (origin, path) => JSON.stringify([origin, path]), reserved = /* @__PURE__ */ new Map(), reserve = (origin, path, value) => {
6441
6476
  let key = outputKey(origin, path);
6442
6477
  reserved.has(key) || reserved.set(key, value);
@@ -6446,11 +6481,18 @@ function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest) {
6446
6481
  kind: "group",
6447
6482
  owner: group.key
6448
6483
  });
6449
- for (let page of manifest.pages)
6450
- reserve(effectiveOrigin(manifest, page.group), page.path, {
6451
- kind: "page",
6452
- owner: `${page.group}/${page.fileID}`
6453
- });
6484
+ let candidates = content?.candidateRoutes ?? manifest.routes;
6485
+ for (let route of candidates) {
6486
+ let owner = content?.files.find(
6487
+ (item) => item.group === route.group && item.fileID === route.fileID
6488
+ )?.absolutePath ?? `${route.group}/${route.fileID}`, paths = [route.path];
6489
+ config.agent?.enabled !== !1 && config.agent?.markdownRoutes !== !1 && paths.push(`${route.path}.md`);
6490
+ for (let path of paths)
6491
+ reserve(effectiveOrigin(manifest, route.group), path, {
6492
+ kind: "page",
6493
+ owner
6494
+ });
6495
+ }
6454
6496
  let origins = /* @__PURE__ */ new Set([
6455
6497
  manifest.site.origin,
6456
6498
  ...Object.keys(manifest.groups).map(
@@ -6458,11 +6500,24 @@ function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest) {
6458
6500
  )
6459
6501
  ]);
6460
6502
  for (let asset of Object.values(mediaManifest?.assets ?? {}))
6461
- for (let origin of origins)
6503
+ for (let origin of origins) {
6504
+ let previous = reserved.get(outputKey(origin, asset.outputPath));
6505
+ if (previous)
6506
+ throw new AppError({
6507
+ code: "FRAMEWORK_OUTPUT_PATH_COLLISION",
6508
+ message: `Media path "${asset.outputPath}" from ${asset.sourcePath} collides with ${previous.kind} ${previous.owner}.`,
6509
+ fix: "Rename the source asset or change the page slug.",
6510
+ details: {
6511
+ path: asset.outputPath,
6512
+ collisionOwner: previous.owner,
6513
+ asset: asset.sourcePath
6514
+ }
6515
+ });
6462
6516
  reserve(origin, asset.outputPath, {
6463
6517
  kind: "media",
6464
6518
  owner: asset.sourcePath
6465
6519
  });
6520
+ }
6466
6521
  let outputRoutes = /* @__PURE__ */ new Map();
6467
6522
  for (let plan of plans) {
6468
6523
  let group = plan.group ? manifest.groups[plan.group] : void 0;
@@ -6477,7 +6532,7 @@ function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest) {
6477
6532
  if (collision)
6478
6533
  throw new AppError({
6479
6534
  code: "FRAMEWORK_OUTPUT_PATH_COLLISION",
6480
- message: `Generated output path "${plan.path}" collides with ${collision.kind} "${collision.owner}" for ${origin ?? "this local build"}`,
6535
+ message: `Generated ${plan.kind} output path "${plan.path}" collides with ${collision.kind} "${collision.owner}" for ${origin ?? "this local build"}. Change the output path or page slug.`,
6481
6536
  details: {
6482
6537
  path: plan.path,
6483
6538
  origin,
@@ -6502,7 +6557,7 @@ function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest) {
6502
6557
  outputRoutes.set(routePath, { path: plan.path, origin });
6503
6558
  }
6504
6559
  }
6505
- async function appFilePlans(config, manifest, fonts, groupThemes, privateFiles = [], notFoundScopes = []) {
6560
+ async function appFilePlans(config, manifest, fonts, groupThemes, privateFiles = [], notFoundScopes = [], outputPlans = planGeneratedOutputs(config, manifest)) {
6506
6561
  let hasRootMountedGroup = Object.values(config.groups).some(
6507
6562
  (group) => group.key !== ROOT_CONTENT_GROUP_KEY && group.path === "/"
6508
6563
  ), plans = [
@@ -6549,6 +6604,10 @@ async function appFilePlans(config, manifest, fonts, groupThemes, privateFiles =
6549
6604
  JSON.stringify(FRAMEWORK_PROXY_MATCHER)
6550
6605
  )
6551
6606
  },
6607
+ {
6608
+ path: "app/agent-user-agents.ts",
6609
+ content: BANNER + await readFile6(resolve7(SHIMS_DIR, "agent-user-agents.ts"), "utf-8")
6610
+ },
6552
6611
  {
6553
6612
  path: "app/publish-os-theme-provider.tsx",
6554
6613
  content: BANNER + await readFile6(
@@ -6587,9 +6646,7 @@ ${notFoundLayouts.join(`
6587
6646
  `)}
6588
6647
  }`
6589
6648
  })
6590
- });
6591
- let outputPlans = planGeneratedOutputs(config, manifest);
6592
- validateGeneratedOutputPaths(config, manifest, outputPlans);
6649
+ }), validateGeneratedOutputPaths(config, manifest, outputPlans);
6593
6650
  for (let output of outputPlans)
6594
6651
  plans.push({
6595
6652
  path: outputRoutePathForPlan(output, config),
@@ -6661,16 +6718,38 @@ ${notFoundLayouts.join(`
6661
6718
  function withPublicNavigationTargets(nodes, manifest) {
6662
6719
  return nodes.map((node) => node.type === "page" ? {
6663
6720
  ...node,
6664
- href: navigationUrl(manifest, node.path, node.group)
6721
+ href: navigationUrl(manifest, node.page.path, node.page.group),
6722
+ ...node.children ? { children: withPublicNavigationTargets(node.children, manifest) } : {}
6723
+ } : node.type === "group" ? {
6724
+ ...node,
6725
+ ...node.presentation === "page" ? { href: navigationUrl(manifest, node.route, node.groupKey) } : {},
6726
+ children: withPublicNavigationTargets(node.children, manifest)
6665
6727
  } : node.type === "section" || node.type === "folder" ? {
6666
6728
  ...node,
6667
- ...node.group && node.path ? { href: navigationUrl(manifest, node.path, node.group) } : {},
6668
6729
  children: withPublicNavigationTargets(node.children, manifest)
6730
+ } : node.type === "view" ? {
6731
+ ...node,
6732
+ items: withPublicNavigationTargets(node.items, manifest)
6669
6733
  } : node);
6670
6734
  }
6671
6735
  async function writeComposedApplication(options) {
6672
6736
  options.signal?.throwIfAborted();
6673
- let preparedSource = options.preparedSource, config = preparedSource.config, manifest = withAttribution(preparedSource.manifest, {
6737
+ let preparedSource = options.preparedSource;
6738
+ for (let file of preparedSource.content.files)
6739
+ for (let field of ["publishedAt", "updatedAt"]) {
6740
+ let value = file.frontmatter[field];
6741
+ if (value === void 0 || authoredDate(value)) continue;
6742
+ let path = relative4(options.publishOsDir, file.absolutePath).replaceAll(
6743
+ "\\",
6744
+ "/"
6745
+ );
6746
+ options.onWarning?.({
6747
+ code: "CONTENT_DATE_INVALID",
6748
+ file: path,
6749
+ message: `Invalid ${field} in ${path}; use YYYY-MM-DD or a timestamp with a timezone.`
6750
+ });
6751
+ }
6752
+ let config = preparedSource.config, manifest = withAttribution(preparedSource.manifest, {
6674
6753
  config,
6675
6754
  projectSlug: options.projectSlug
6676
6755
  }), connectedOrigin = options.hostedSite ? normalizeHostedSiteUrl(options.hostedSite.url) : void 0, rootOwnedGroups = new Set(
@@ -6682,9 +6761,16 @@ async function writeComposedApplication(options) {
6682
6761
  options.hostedSite && (manifest.site.name = config.site?.name?.trim() || normalizeHostedSiteName(options.hostedSite.name)), manifest.site.origin = options.previewUrl ?? connectedOrigin ?? config.origin;
6683
6762
  for (let [groupKey, group] of Object.entries(manifest.groups))
6684
6763
  group.origin = options.previewUrl ? options.previewUrl : options.hostedSite ? config.groups[groupKey]?.origin ?? (groupKey === ROOT_CONTENT_GROUP_KEY ? void 0 : config.origin) ?? connectedOrigin : void 0;
6685
- manifest.tree = withPublicNavigationTargets(manifest.tree, manifest);
6686
- for (let group of Object.values(manifest.groups))
6687
- group.tree = withPublicNavigationTargets(group.tree, manifest);
6764
+ let navigationByRoot = /* @__PURE__ */ new Map();
6765
+ for (let group of Object.values(manifest.groups)) {
6766
+ let rootGroup = group.navigation.rootGroup;
6767
+ navigationByRoot.has(rootGroup) || navigationByRoot.set(rootGroup, {
6768
+ ...group.navigation,
6769
+ items: withPublicNavigationTargets(group.navigation.items, manifest)
6770
+ });
6771
+ let navigation = navigationByRoot.get(rootGroup);
6772
+ navigation && (group.navigation = navigation);
6773
+ }
6688
6774
  let { faviconsAndLogos, media } = preparedSource.assets, prepareAssets = (requests) => prepareFontAssets({
6689
6775
  requests,
6690
6776
  cacheDirectory: options.fontCacheDirectory,
@@ -6729,7 +6815,13 @@ async function writeComposedApplication(options) {
6729
6815
  message
6730
6816
  })
6731
6817
  }), sourceDir = resolve7(options.outputDir, "source"), outputPlans = planGeneratedOutputs(config, manifest);
6732
- validateGeneratedOutputPaths(config, manifest, outputPlans, media.manifest);
6818
+ validateGeneratedOutputPaths(
6819
+ config,
6820
+ manifest,
6821
+ outputPlans,
6822
+ media.manifest,
6823
+ preparedSource.content
6824
+ );
6733
6825
  let { defaultsUsed, sourceOverrides } = await overlaySource(
6734
6826
  options.publishOsDir,
6735
6827
  sourceDir,
@@ -6769,14 +6861,39 @@ async function writeComposedApplication(options) {
6769
6861
  `${JSON.stringify(ogManifestMetadata(og), null, 2)}
6770
6862
  `
6771
6863
  );
6772
- let pageArtifactConversions = convertPublishedPageBodies(manifest), markdownRouteArtifacts = markdownArtifacts(
6864
+ let includeDrafts = !!options.previewUrl, pageArtifactConversions = convertPageBodies(manifest, includeDrafts), markdownRouteArtifacts = markdownArtifacts(
6773
6865
  manifest,
6774
6866
  outputPlans,
6775
6867
  pageArtifactConversions,
6776
- rootOwnedGroups
6868
+ rootOwnedGroups,
6869
+ includeDrafts
6777
6870
  ), markdownPaths = config.agent?.enabled === !1 || config.agent?.markdownRoutes === !1 ? /* @__PURE__ */ new Set() : new Set(
6778
6871
  markdownRouteArtifacts.map((artifact) => artifact.markdownPath)
6779
- ), notFoundScopes = createNotFoundScopes({
6872
+ ), expandedLlmsIndexes = expandLlmsIndexOutputPlans({
6873
+ manifest,
6874
+ outputPlans,
6875
+ markdownPaths: new Map(
6876
+ markdownRouteArtifacts.filter((artifact) => markdownPaths.has(artifact.markdownPath)).map((artifact) => [artifact.htmlPath, artifact.markdownPath])
6877
+ )
6878
+ }), expandedLlmsFull = expandLlmsFullOutputPlans({
6879
+ manifest,
6880
+ outputPlans: expandedLlmsIndexes.outputPlans,
6881
+ artifacts: markdownRouteArtifacts,
6882
+ markdownPaths,
6883
+ onWarning: options.onWarning
6884
+ });
6885
+ outputPlans = expandSitemapOutputPlans(
6886
+ manifest,
6887
+ expandedLlmsFull.outputPlans,
6888
+ markdownPaths
6889
+ ), validateGeneratedOutputPaths(
6890
+ config,
6891
+ manifest,
6892
+ outputPlans,
6893
+ media.manifest,
6894
+ preparedSource.content
6895
+ );
6896
+ let notFoundScopes = createNotFoundScopes({
6780
6897
  manifest,
6781
6898
  outputs: outputPlans,
6782
6899
  rootOwnedGroups,
@@ -6798,7 +6915,9 @@ async function writeComposedApplication(options) {
6798
6915
  manifest,
6799
6916
  outputPlans,
6800
6917
  markdownRouteArtifacts,
6801
- markdownPaths
6918
+ markdownPaths,
6919
+ expandedLlmsFull.bodies,
6920
+ expandedLlmsIndexes.bodies
6802
6921
  )
6803
6922
  );
6804
6923
  let markdownRouteArtifactShards = createMarkdownArtifactShards(
@@ -6834,7 +6953,8 @@ async function writeComposedApplication(options) {
6834
6953
  ...file,
6835
6954
  content: BANNER + file.content
6836
6955
  })),
6837
- notFoundScopes
6956
+ notFoundScopes,
6957
+ outputPlans
6838
6958
  ), componentManifest = await createComponentManifest({
6839
6959
  applicationFiles: appPlans,
6840
6960
  runtimeDistDir: RUNTIME_DIST_DIR,