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

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 (223) hide show
  1. package/compiler-assets/nextjs/defaults/group/layout.tsx +1 -5
  2. package/compiler-assets/nextjs/defaults/group/page.tsx +31 -22
  3. package/compiler-assets/nextjs/defaults/project/next.config.mjs +19 -10
  4. package/compiler-assets/nextjs/defaults/project/tsconfig.json.template +10 -0
  5. package/compiler-assets/nextjs/defaults/root/css/components/code-block.css +18 -0
  6. package/compiler-assets/nextjs/shims/document.tsx +38 -0
  7. package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +2 -0
  8. package/compiler-assets/nextjs/shims/global-layout.tsx +8 -42
  9. package/compiler-assets/nextjs/shims/global-not-found.tsx +69 -0
  10. package/compiler-assets/nextjs/shims/markdown-route.ts +2 -0
  11. package/compiler-assets/nextjs/shims/metadata-icons.tsx +39 -0
  12. package/compiler-assets/nextjs/shims/not-found-content.tsx +53 -0
  13. package/compiler-assets/nextjs/shims/not-found-navigation.tsx +60 -0
  14. package/compiler-assets/nextjs/shims/proxy.ts +126 -60
  15. package/compiler-assets/nextjs/shims/site-fenced-code-block.tsx +2 -6
  16. package/compiler-assets/nextjs/shims/site-mdx-components.tsx +28 -16
  17. package/compiler-assets/nextjs/shims/site-mdx.tsx +6 -1
  18. package/dist/{build-operation-2JAYBSKF.js → build-operation-5QMLXF7N.js} +29 -23
  19. package/dist/{button-c3WGnxIj.d.ts → button-BdCScEKi.d.ts} +3 -3
  20. package/dist/capture-local-source-WGGDIUU2.js +258 -0
  21. package/dist/chunk-22RSSUNV.js +13 -0
  22. package/dist/{chunk-WKTEO6RS.js → chunk-4BC2L2KA.js} +46 -12
  23. package/dist/{chunk-WDY4IK6Z.js → chunk-5J3YUNRY.js} +3 -10
  24. package/dist/chunk-5KAD7W53.js +248 -0
  25. package/dist/{chunk-DWA3ZRGW.js → chunk-6C65KV7O.js} +2 -2
  26. package/dist/{chunk-GGKFQ3MA.js → chunk-6D2E6UMD.js} +144 -70
  27. package/dist/{chunk-7OGTPSKI.js → chunk-7NY22B5U.js} +8 -3
  28. package/dist/chunk-EFO6AWXN.js +19 -0
  29. package/dist/{chunk-726GV2NF.js → chunk-F3AGNDF4.js} +100 -67
  30. package/dist/chunk-GBPLER5H.js +426 -0
  31. package/dist/chunk-IL4RJOKI.js +186 -0
  32. package/dist/{chunk-DLDFHU7Y.js → chunk-NNBJZPFM.js} +4 -2
  33. package/dist/{chunk-VO2I7MTM.js → chunk-ORIUWJBQ.js} +8 -6
  34. package/dist/{chunk-H6XILMZX.js → chunk-OXOFRXEI.js} +1806 -831
  35. package/dist/chunk-UYGCOWRF.js +1709 -0
  36. package/dist/{chunk-YLTOLYWM.js → chunk-WL75GQ5M.js} +6 -18
  37. package/dist/compiler.d.ts +15 -2
  38. package/dist/compiler.js +90 -21
  39. package/dist/components/blocks/agent-actions.d.ts +81 -0
  40. package/dist/components/blocks/agent-actions.js +594 -0
  41. package/dist/components/blocks/toc.d.ts +43 -0
  42. package/dist/components/blocks/toc.js +136 -0
  43. package/dist/components/ui/button.d.ts +5 -0
  44. package/dist/components/ui/button.js +94 -0
  45. package/dist/compose-CVBJRAF6.js +16 -0
  46. package/dist/config/createNextConfig.js +17 -3
  47. package/dist/{dev-operation-TZDVJCN7.js → dev-operation-OSHHWRWK.js} +121 -81
  48. package/dist/{framework-selection-X4U6Y6QC.js → framework-selection-ZVO323PK.js} +3 -2
  49. package/dist/ide-support-CU6Z5PVR.js +16 -0
  50. package/dist/index.d.ts +40 -1
  51. package/dist/jsx-dev-runtime.d.ts +6 -0
  52. package/dist/jsx-dev-runtime.js +100 -0
  53. package/dist/jsx-runtime.d.ts +7 -0
  54. package/dist/jsx-runtime.js +97 -0
  55. package/dist/kernel/components/blocks/AgentActions.d.ts +35 -6
  56. package/dist/kernel/components/blocks/AgentActions.js +383 -142
  57. package/dist/kernel/components/blocks/agent-actions.d.ts +5 -0
  58. package/dist/kernel/components/blocks/agent-actions.js +594 -0
  59. package/dist/kernel/components/blocks/animated-icon-swap.js +1 -1
  60. package/dist/kernel/components/blocks/color-palette.js +4 -4
  61. package/dist/kernel/components/blocks/logo-block.js +1 -1
  62. package/dist/kernel/components/blocks/nav-breadcrumb.js +2 -2
  63. package/dist/kernel/components/blocks/navbar.js +17 -17
  64. package/dist/kernel/components/blocks/navigation-progress.js +2 -2
  65. package/dist/kernel/components/blocks/next-prev.js +1 -1
  66. package/dist/kernel/components/blocks/page-list.js +2 -2
  67. package/dist/kernel/components/blocks/search.d.ts +1 -1
  68. package/dist/kernel/components/blocks/search.js +16 -17
  69. package/dist/kernel/components/blocks/sidebar-node.js +1 -1
  70. package/dist/kernel/components/blocks/sidebar.js +7 -4
  71. package/dist/kernel/components/blocks/theme-toggle.d.ts +1 -1
  72. package/dist/kernel/components/blocks/theme-toggle.js +5 -5
  73. package/dist/kernel/components/blocks/toc-client.d.ts +40 -0
  74. package/dist/kernel/components/blocks/toc-client.js +495 -0
  75. package/dist/kernel/components/blocks/toc.d.ts +19 -1
  76. package/dist/kernel/components/blocks/toc.js +121 -179
  77. package/dist/kernel/components/mdx/components/blockquote.js +1 -1
  78. package/dist/kernel/components/mdx/components/callout.js +1 -1
  79. package/dist/kernel/components/mdx/components/code-block.js +2 -2
  80. package/dist/kernel/components/mdx/components/copy-code-button.js +5 -4
  81. package/dist/kernel/components/mdx/components/figure-caption.d.ts +1 -1
  82. package/dist/kernel/components/mdx/components/figure-caption.js +3 -2
  83. package/dist/kernel/components/mdx/components/figure.d.ts +6 -2
  84. package/dist/kernel/components/mdx/components/figure.js +23 -13
  85. package/dist/kernel/components/mdx/components/footnotes.js +1 -1
  86. package/dist/kernel/components/mdx/components/heading-copy-link.js +1 -1
  87. package/dist/kernel/components/mdx/components/heading.js +6 -3
  88. package/dist/kernel/components/mdx/components/hr.js +2 -2
  89. package/dist/kernel/components/mdx/components/image-zoom.js +1 -1
  90. package/dist/kernel/components/mdx/components/image.d.ts +1 -1
  91. package/dist/kernel/components/mdx/components/image.js +21 -16
  92. package/dist/kernel/components/mdx/components/inline-code.js +1 -1
  93. package/dist/kernel/components/mdx/components/kbd.js +2 -2
  94. package/dist/kernel/components/mdx/components/link.d.ts +3 -3
  95. package/dist/kernel/components/mdx/components/link.js +2 -2
  96. package/dist/kernel/components/mdx/components/paragraph.js +1 -1
  97. package/dist/kernel/components/mdx/components/strikethrough.js +1 -1
  98. package/dist/kernel/components/mdx/components/table.js +2 -2
  99. package/dist/kernel/components/mdx/components/task-list.js +1 -1
  100. package/dist/kernel/components/mdx/components/video-player.d.ts +6 -2
  101. package/dist/kernel/components/mdx/components/video-player.js +6 -2
  102. package/dist/kernel/components/mdx/components/video.d.ts +5 -3
  103. package/dist/kernel/components/mdx/components/video.js +7 -5
  104. package/dist/kernel/components/mdx/error-boundary.d.ts +1 -1
  105. package/dist/kernel/components/mdx/error-boundary.js +2 -2
  106. package/dist/kernel/components/mdx/error-fallback.js +1 -1
  107. package/dist/kernel/components/mdx/mdx.d.ts +20 -10
  108. package/dist/kernel/components/mdx/mdx.js +231 -34
  109. package/dist/kernel/components/mdx/rehype-heading-ids.d.ts +1 -0
  110. package/dist/kernel/components/mdx/rehype-heading-ids.js +5 -3
  111. package/dist/kernel/components/mdx/types.d.ts +1 -1
  112. package/dist/kernel/components/next-image.d.ts +12 -0
  113. package/dist/kernel/components/next-image.js +61 -0
  114. package/dist/kernel/components/ui/accordion.js +1 -1
  115. package/dist/kernel/components/ui/breadcrumb-trail.d.ts +2 -2
  116. package/dist/kernel/components/ui/breadcrumb-trail.js +2 -2
  117. package/dist/kernel/components/ui/breadcrumb.d.ts +1 -1
  118. package/dist/kernel/components/ui/breadcrumb.js +4 -4
  119. package/dist/kernel/components/ui/button-group.d.ts +1 -1
  120. package/dist/kernel/components/ui/button-group.js +3 -3
  121. package/dist/kernel/components/ui/button.d.ts +56 -4
  122. package/dist/kernel/components/ui/button.js +43 -39
  123. package/dist/kernel/components/ui/card.js +1 -1
  124. package/dist/kernel/components/ui/dialog.js +1 -1
  125. package/dist/kernel/components/ui/drawer.js +1 -1
  126. package/dist/kernel/components/ui/input.d.ts +2 -2
  127. package/dist/kernel/components/ui/input.js +1 -1
  128. package/dist/kernel/components/ui/kbd.d.ts +1 -1
  129. package/dist/kernel/components/ui/kbd.js +1 -1
  130. package/dist/kernel/components/ui/link.d.ts +8 -2
  131. package/dist/kernel/components/ui/link.js +2 -2
  132. package/dist/kernel/components/ui/menu.js +1 -1
  133. package/dist/kernel/components/ui/mod-key.js +2 -2
  134. package/dist/kernel/components/ui/navigation-menu.js +1 -1
  135. package/dist/kernel/components/ui/popover.js +1 -1
  136. package/dist/kernel/components/ui/progress.js +1 -1
  137. package/dist/kernel/components/ui/scroll-area.d.ts +8 -8
  138. package/dist/kernel/components/ui/scroll-area.js +1 -1
  139. package/dist/kernel/components/ui/select.js +1 -1
  140. package/dist/kernel/components/ui/separator.js +1 -1
  141. package/dist/kernel/components/ui/table.js +1 -1
  142. package/dist/kernel/components/ui/tabs.js +1 -1
  143. package/dist/kernel/components/ui/tag.js +1 -1
  144. package/dist/kernel/components/ui/text.d.ts +1 -1
  145. package/dist/kernel/components/ui/text.js +1 -1
  146. package/dist/kernel/components/ui/toast.js +1 -1
  147. package/dist/kernel/components/ui/tooltip.js +1 -1
  148. package/dist/kernel/defaults.registry.d.ts +2 -15
  149. package/dist/kernel/defaults.registry.js +2 -17
  150. package/dist/kernel/routes/agent/markdown.d.ts +3 -0
  151. package/dist/kernel/routes/agent/markdown.js +219 -48
  152. package/dist/kernel/routes/layouts/group.d.ts +1 -1
  153. package/dist/kernel/routes/layouts/group.js +5 -7
  154. package/dist/kernel/routes/metadata/feed.d.ts +1 -1
  155. package/dist/kernel/routes/metadata/feed.js +109 -83
  156. package/dist/kernel/routes/metadata/llms.d.ts +11 -3
  157. package/dist/kernel/routes/metadata/llms.js +154 -57
  158. package/dist/kernel/routes/metadata/og-image.js +1 -1
  159. package/dist/kernel/routes/metadata/robots.d.ts +1 -1
  160. package/dist/kernel/routes/metadata/sitemap.d.ts +22 -3
  161. package/dist/kernel/routes/metadata/sitemap.js +183 -43
  162. package/dist/kernel/routes/not-found.d.ts +34 -0
  163. package/dist/kernel/routes/not-found.js +213 -0
  164. package/dist/kernel/routes/pages/group.d.ts +2 -2
  165. package/dist/kernel/routes/pages/group.js +1 -1
  166. package/dist/kernel/routes/pages/leaf.d.ts +30 -3
  167. package/dist/kernel/routes/pages/leaf.js +5 -4
  168. package/dist/kernel/routes/pages/root.d.ts +2 -2
  169. package/dist/kernel/routes/pages/root.js +1 -1
  170. package/dist/keyboard-shortcuts.js +1 -1
  171. package/dist/route-metadata-og-image-BeKKFHg7.d.ts +58 -0
  172. package/dist/search.js +7 -9
  173. package/dist/theme-scope.js +1 -1
  174. package/dist/{types-BzkZty3r.d.ts → types-J2w_puPF.d.ts} +1 -20
  175. package/dist/types-sM2g9nW0.d.ts +12 -0
  176. package/dist/types.d.ts +1 -0
  177. package/dist/utils/heading-ids.js +30 -11
  178. package/dist/utils/toc-selection.d.ts +7 -0
  179. package/dist/utils/toc-selection.js +14 -0
  180. package/dist/validate-local-source-SV44DBQT.js +16 -0
  181. package/dist/validate-publish-os-source-NZJF3TB2.js +14 -0
  182. package/init/seed/minimal/publish.os/assets/blog/hello-world.svg +13 -0
  183. package/init/seed/minimal/publish.os/assets/favicon.dark.svg +13 -0
  184. package/init/seed/minimal/publish.os/assets/favicon.svg +13 -0
  185. package/init/seed/minimal/publish.os/blog/content/hello-world.mdx +48 -0
  186. package/init/seed/minimal/publish.os/blog/content/series-a.mdx +11 -0
  187. package/init/seed/minimal/publish.os/blog/index.tsx +82 -0
  188. package/init/seed/minimal/publish.os/blog/layout.tsx +13 -0
  189. package/init/seed/minimal/publish.os/blog/page.tsx +76 -0
  190. package/init/seed/minimal/publish.os/components/blog/copy-link.tsx +64 -0
  191. package/init/seed/minimal/publish.os/docs/content/get-started.mdx +2 -3
  192. package/init/seed/minimal/publish.os/docs/content/groups.mdx +5 -6
  193. package/init/seed/minimal/publish.os/publish.os.config.json +6 -2
  194. package/init/seed/minimal/publish.os/theme.css +3 -3
  195. package/init/seed/minimal/seed.json +11 -4
  196. package/init/seed/scratch/publish.os/docs/content/get-started.mdx +1 -1
  197. package/init/seed/scratch/publish.os/publish.os.config.json +1 -1
  198. package/package.json +45 -20
  199. package/release/app/phosphor/package.json +3 -3
  200. package/release/app/phosphor/pnpm-lock.yaml.template +88 -75
  201. package/release/compiler/package.json +5 -3
  202. package/release/compiler/pnpm-lock.yaml.template +118 -74
  203. package/release/release.json +6 -6
  204. package/dist/button-CtzUJB8u.d.ts +0 -11
  205. package/dist/chunk-535TM4A5.js +0 -1455
  206. package/dist/chunk-B2V22WRT.js +0 -297
  207. package/dist/chunk-NECL5IBJ.js +0 -318
  208. package/dist/chunk-SVYGXXYR.js +0 -36
  209. package/dist/chunk-UF6ATARM.js +0 -1100
  210. package/dist/compose-NN23UJGC.js +0 -13
  211. package/dist/ide-support-ZNW7ODFY.js +0 -15
  212. package/dist/kernel/components/blocks/design-system-preview.d.ts +0 -61
  213. package/dist/kernel/components/blocks/design-system-preview.js +0 -667
  214. package/dist/kernel/components/blocks/link-playground.d.ts +0 -5
  215. package/dist/kernel/components/blocks/link-playground.js +0 -744
  216. package/dist/kernel/components/blocks/navigation-progress-playground.d.ts +0 -5
  217. package/dist/kernel/components/blocks/navigation-progress-playground.js +0 -629
  218. package/dist/kernel/components/ui/theme-toggle.d.ts +0 -24
  219. package/dist/kernel/components/ui/theme-toggle.js +0 -53
  220. package/dist/validate-local-source-XRL2Y7XE.js +0 -14
  221. package/dist/validate-publish-os-source-DAPZ4QGE.js +0 -12
  222. package/init/seed/minimal/publish.os/assets/favicon/favicon.dark.png +0 -0
  223. package/init/seed/minimal/publish.os/assets/favicon/favicon.png +0 -0
@@ -1,52 +1,70 @@
1
+ import {
2
+ assertFrameworkArchiveIdentity,
3
+ assertFrameworkSelection,
4
+ readCheckoutTypeScriptConfig
5
+ } from "./chunk-NNBJZPFM.js";
1
6
  import {
2
7
  ROOT_CONTENT_GROUP_KEY,
8
+ admitThemeImageResource,
3
9
  assertSourceDependency,
4
- cssImports,
5
- decodeCssPath,
6
10
  faviconsAndLogosManifestModule,
7
11
  groupedMdxExports,
8
12
  mdxComponentExportName,
13
+ mdxIconPackage,
14
+ mediaAssetBudgets,
9
15
  mediaManifestModule,
10
16
  namedValueExports,
17
+ nextjs_runtime_surface_default,
11
18
  normalizeUrlPath,
19
+ readMdxIconImports,
12
20
  replacedMdxSourceFiles,
13
- writeFaviconsAndLogosOutputs,
21
+ supportedRuntimeComponentAliases,
14
22
  writeMediaOutputs
15
- } from "./chunk-535TM4A5.js";
16
- import {
17
- assertFrameworkArchiveIdentity,
18
- assertFrameworkSelection,
19
- readCheckoutTypeScriptConfig
20
- } from "./chunk-DLDFHU7Y.js";
23
+ } from "./chunk-UYGCOWRF.js";
21
24
  import {
22
- ASSET_PREFIX,
23
25
  FRAMEWORK_PREFIX,
24
26
  FRAMEWORK_PROXY_MATCHER,
25
27
  IMAGE_OPTIMIZER_PATH,
28
+ INTERNAL_PREFIX,
26
29
  NEXT_IMAGE_OPTIMIZER_PATH,
27
30
  OG_SERVICE_PREFIX,
28
- PUBLIC_ASSET_DIRECTORY,
29
31
  PUBLIC_FONT_DIRECTORY,
30
- PUBLIC_PAGEFIND_DIRECTORY,
32
+ PUBLIC_INTERNAL_DIRECTORY,
33
+ PUBLIC_SEARCH_DIRECTORY,
34
+ SOURCE_ASSET_PREFIX,
35
+ isFrameworkNamespacePath
36
+ } from "./chunk-EFO6AWXN.js";
37
+ import {
38
+ cssImports,
39
+ decodeCssPath,
40
+ ignoredMediaDocumentWarning,
41
+ ignoredMediaPathWarning,
42
+ isHiddenMediaPath,
43
+ mediaFileTooLarge,
44
+ readBoundedMediaFile,
45
+ themeResourceByteLimit
46
+ } from "./chunk-5KAD7W53.js";
47
+ import {
31
48
  isApprovedPublishOsPath,
32
49
  isDescendant,
33
50
  isExcludedSourcePath,
34
- isFrameworkNamespacePath,
35
51
  isReservedFrameworkPath,
36
52
  isSameOrDescendant
37
- } from "./chunk-YLTOLYWM.js";
53
+ } from "./chunk-WL75GQ5M.js";
38
54
  import {
39
- AppError,
40
55
  readInstalledFramework,
41
56
  readInstalledFrameworkLocation
42
- } from "./chunk-WDY4IK6Z.js";
57
+ } from "./chunk-5J3YUNRY.js";
58
+ import {
59
+ AppError
60
+ } from "./chunk-22RSSUNV.js";
43
61
 
44
62
  // src/lib/compiler/nextjs/compose.ts
45
63
  import { createHash as createHash4 } from "crypto";
46
64
  import { existsSync } from "fs";
47
65
  import {
48
66
  cp,
49
- lstat as lstat2,
67
+ lstat as lstat3,
50
68
  mkdir as mkdir6,
51
69
  mkdtemp as mkdtemp2,
52
70
  readdir as readdir3,
@@ -69,6 +87,84 @@ import {
69
87
  import { googleFontsMetadata as googleFontsMetadata2 } from "next/dist/compiled/@next/font/dist/google/google-fonts-metadata.js";
70
88
  import ts3 from "typescript";
71
89
 
90
+ // src/lib/assets/map-source-asset-css.ts
91
+ import postcss from "postcss";
92
+ 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
+ var IMAGE_SET_FUNCTIONS = /* @__PURE__ */ new Set(["image-set", "-webkit-image-set"]);
128
+ function mapCssUrls(value) {
129
+ let parsed = valueParser(value);
130
+ return parsed.walk((node) => {
131
+ if (node.type !== "function") return;
132
+ let name = node.value.toLowerCase();
133
+ if (name === "url") {
134
+ let url = node.nodes.find(
135
+ (part) => part.type === "word" || part.type === "string"
136
+ );
137
+ if (url) {
138
+ let mapped = mapSourceAssetUrl(url.value.trim());
139
+ typeof mapped == "string" && (url.value = mapped);
140
+ }
141
+ return !1;
142
+ }
143
+ if (IMAGE_SET_FUNCTIONS.has(name))
144
+ for (let candidate of node.nodes) {
145
+ if (candidate.type !== "string") continue;
146
+ let mapped = mapSourceAssetUrl(candidate.value.trim());
147
+ typeof mapped == "string" && (candidate.value = mapped);
148
+ }
149
+ }), parsed.toString();
150
+ }
151
+ function mapSourceAssetCss(css, sourcePath) {
152
+ let root = postcss.parse(css, { from: sourcePath });
153
+ return root.walkDecls((declaration) => {
154
+ declaration.value = mapCssUrls(declaration.value);
155
+ }), root.walkAtRules((atRule) => {
156
+ if (atRule.name.toLowerCase() !== "import") return;
157
+ let parsed = valueParser(atRule.params), source = parsed.nodes.find(
158
+ (node) => node.type === "word" || node.type === "string"
159
+ );
160
+ if (source) {
161
+ let mapped = mapSourceAssetUrl(source.value);
162
+ typeof mapped == "string" && (source.value = mapped);
163
+ }
164
+ atRule.params = mapCssUrls(parsed.toString());
165
+ }), root.toString();
166
+ }
167
+
72
168
  // src/lib/compiler/deployment/deployment-evidence.ts
73
169
  import { mkdir, writeFile } from "fs/promises";
74
170
  import { join } from "path";
@@ -713,6 +809,58 @@ function tabHandler(node, context) {
713
809
  ] : children;
714
810
  }
715
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
+
716
864
  // src/lib/compiler/markdown/handlers/inline.ts
717
865
  function iconHandler(node, context) {
718
866
  handlerDiagnostic(node, context);
@@ -764,18 +912,33 @@ function imageHandler(node, context, kind) {
764
912
  let caption = firstAttribute(node, ["caption"], context), image = {
765
913
  type: "image",
766
914
  url: context.resolveMediaUrl(src),
767
- alt: firstAttribute(node, ["alt", "title", "aria-label"], context) ?? "",
915
+ alt: getAttribute(node, "alt", context) ?? firstAttribute(node, ["title", "aria-label"], context) ?? "",
768
916
  title: firstAttribute(node, ["title"], context)
769
917
  };
770
- return kind === "inline" ? [image] : [
918
+ return kind === "inline" ? [image, ...caption ? [text(` ${caption}`)] : []] : [
771
919
  ...paragraphBlock([image]),
772
- ...caption ? paragraphBlock([{ type: "emphasis", children: [text(caption)] }]) : []
920
+ ...caption ? paragraphBlock([text(caption)]) : []
773
921
  ];
774
922
  }
775
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) {
776
937
  handlerDiagnostic(node, context);
777
- let children = asPhrasing(context.transformChildrenAsBlocks(node, context));
778
- return children.length > 0 ? [{ type: "paragraph", children: [{ type: "emphasis", children }] }] : [];
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)]) : []];
779
942
  }
780
943
  function videoHandler(node, context, kind) {
781
944
  let src = firstAttribute(node, ["src", "url", "href"], context);
@@ -785,8 +948,11 @@ function videoHandler(node, context, kind) {
785
948
  type: "link",
786
949
  url: context.resolveMediaUrl(src),
787
950
  children: [text(label)]
788
- }, children = [text("Video: "), link];
789
- return kind === "inline" ? children : paragraphBlock(children);
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
+ ];
790
956
  }
791
957
 
792
958
  // src/lib/compiler/markdown/handlers/passthrough.ts
@@ -814,9 +980,29 @@ var defaultHandlers = [
814
980
  transform: calloutHandler
815
981
  },
816
982
  {
817
- componentNames: ["codeblock", "code"],
983
+ componentNames: ["codeblock"],
818
984
  transform: codeBlockHandler
819
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
+ },
820
1006
  {
821
1007
  componentNames: [
822
1008
  "codetab",
@@ -830,10 +1016,10 @@ var defaultHandlers = [
830
1016
  },
831
1017
  {
832
1018
  componentNames: ["figure"],
833
- transform: passthroughBlockHandler
1019
+ transform: figureHandler
834
1020
  },
835
1021
  {
836
- componentNames: ["figcaption", "figurecaption"],
1022
+ componentNames: ["figcaption", "figurecaption", "figure.caption"],
837
1023
  transform: figureCaptionHandler
838
1024
  },
839
1025
  {
@@ -898,6 +1084,12 @@ function transformChildrenAsPhrasing(node, context) {
898
1084
  );
899
1085
  }
900
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
+ }
901
1093
  let componentName = normalizeComponentName(node.name), handler = handlersByName.get(componentName);
902
1094
  if (handler) {
903
1095
  let result = handler.transform(node, context, kind);
@@ -957,9 +1149,18 @@ function transformInlineNode(node, context) {
957
1149
  case "link":
958
1150
  return [{
959
1151
  ...node,
1152
+ url: context.resolveMediaUrl(node.url),
960
1153
  children: transformChildrenAsPhrasing(node, context)
961
1154
  }];
962
1155
  case "image":
1156
+ return [
1157
+ {
1158
+ ...node,
1159
+ url: context.resolveMediaUrl(node.url)
1160
+ }
1161
+ ];
1162
+ case "linkReference":
1163
+ case "imageReference":
963
1164
  return [node];
964
1165
  case "html":
965
1166
  return [node];
@@ -987,6 +1188,20 @@ function transformTableRow(node, context) {
987
1188
  )
988
1189
  };
989
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
+ }
990
1205
  function transformBlockNode(node, context) {
991
1206
  if (node.type === "mdxjsEsm")
992
1207
  return addDiagnostic(context, {
@@ -1002,6 +1217,20 @@ function transformBlockNode(node, context) {
1002
1217
  return transformMdxElement(node, context, "block");
1003
1218
  switch (node.type) {
1004
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
+ }
1005
1234
  let paragraphNode = paragraph(
1006
1235
  transformChildrenAsPhrasing(node, context)
1007
1236
  );
@@ -1047,18 +1276,25 @@ function transformBlockNode(node, context) {
1047
1276
  case "thematicBreak":
1048
1277
  case "html":
1049
1278
  case "yaml":
1050
- case "definition":
1051
1279
  case "footnoteDefinition":
1052
1280
  return [node];
1281
+ case "definition":
1282
+ return [
1283
+ {
1284
+ ...node,
1285
+ url: context.resolveMediaUrl(node.url)
1286
+ }
1287
+ ];
1053
1288
  default:
1054
1289
  return [];
1055
1290
  }
1056
1291
  }
1057
1292
  function convertMdxToMarkdown(options) {
1058
- let resolveMediaUrl = options.resolveMediaUrl ?? ((url) => options.mediaAssets?.[url]?.outputPath ?? url), context = {
1293
+ let context = {
1294
+ importedIcons: /* @__PURE__ */ new Set(),
1059
1295
  collectDiagnostics: options.collectDiagnostics ?? !0,
1060
1296
  diagnostics: [],
1061
- resolveMediaUrl,
1297
+ resolveMediaUrl: (url) => String(mapSourceAssetUrl(url)),
1062
1298
  transformChildrenAsBlocks,
1063
1299
  transformChildrenAsPhrasing,
1064
1300
  unknownComponentMode: options.unknownComponentMode ?? "children"
@@ -1069,6 +1305,13 @@ function convertMdxToMarkdown(options) {
1069
1305
  markdown: stripTopLevelImports(options.source),
1070
1306
  text: ""
1071
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
+ }
1072
1315
  let transformed = {
1073
1316
  type: "root",
1074
1317
  children: parsed.children.flatMap(
@@ -1082,6 +1325,20 @@ function convertMdxToMarkdown(options) {
1082
1325
  };
1083
1326
  }
1084
1327
 
1328
+ // src/lib/compiler/markdown/page-artifact-conversions.ts
1329
+ function convertPublishedPageBodies(manifest) {
1330
+ return new Map(
1331
+ manifest.pages.filter((page) => page.status === "published").map((page) => [
1332
+ page.path,
1333
+ convertMdxToMarkdown({
1334
+ collectDiagnostics: !1,
1335
+ source: page.body,
1336
+ unknownComponentMode: "children"
1337
+ })
1338
+ ])
1339
+ );
1340
+ }
1341
+
1085
1342
  // src/lib/compiler/nextjs/checkout-runtime.ts
1086
1343
  import {
1087
1344
  mkdir as mkdir3,
@@ -1096,8 +1353,11 @@ import { basename, dirname as dirname2, join as join4, relative } from "path";
1096
1353
  import ts from "typescript";
1097
1354
 
1098
1355
  // src/lib/compiler/nextjs/framework-next-config.ts
1099
- function renderFrameworkNextConfig(template) {
1100
- return template.replaceAll("__PUBLISHOS_ASSET_PREFIX__", ASSET_PREFIX).replaceAll("__PUBLISHOS_IMAGE_PATH__", IMAGE_OPTIMIZER_PATH).replaceAll("__PUBLISHOS_NEXT_IMAGE_PATH__", NEXT_IMAGE_OPTIMIZER_PATH);
1356
+ function renderFrameworkNextConfig(template, componentAliases = {}) {
1357
+ return template.replaceAll("__PUBLISHOS_INTERNAL_PREFIX__", INTERNAL_PREFIX).replaceAll("__PUBLISHOS_SOURCE_ASSET_PREFIX__", SOURCE_ASSET_PREFIX).replaceAll("__PUBLISHOS_IMAGE_PATH__", IMAGE_OPTIMIZER_PATH).replaceAll("__PUBLISHOS_NEXT_IMAGE_PATH__", NEXT_IMAGE_OPTIMIZER_PATH).replaceAll(
1358
+ "__PUBLISHOS_COMPONENT_ALIASES__",
1359
+ JSON.stringify(componentAliases)
1360
+ );
1101
1361
  }
1102
1362
 
1103
1363
  // src/lib/compiler/nextjs/checkout-runtime.ts
@@ -1235,6 +1495,7 @@ export default {
1235
1495
  ...base.turbopack,
1236
1496
  root,
1237
1497
  resolveAlias: {
1498
+ ...base.turbopack?.resolveAlias,
1238
1499
  ...aliases,
1239
1500
  "@app/*": "./app/*",
1240
1501
  "@source/*": "./source/*",
@@ -1276,7 +1537,7 @@ function googleFontOutput({
1276
1537
  () => `font-family: '${family}';`
1277
1538
  );
1278
1539
  for (let file of assets.files)
1279
- css = css.split(file.url).join(`${ASSET_PREFIX}/_publishos/fonts/${file.sha256}.${file.format}`);
1540
+ css = css.split(file.url).join(`${INTERNAL_PREFIX}/fonts/${file.sha256}.${file.format}`);
1280
1541
  let metrics = font.adjustFontFallback ? getFallbackFontOverrideMetrics(font.family) : void 0;
1281
1542
  metrics && (css += `
1282
1543
  @font-face { font-family: '${family} Fallback'; src: local('${metrics.fallbackFont}'); ascent-override: ${metrics.ascentOverride}; descent-override: ${metrics.descentOverride}; line-gap-override: ${metrics.lineGapOverride}; size-adjust: ${metrics.sizeAdjust}; }
@@ -1298,7 +1559,7 @@ function googleFontOutput({
1298
1559
  else if (font.preload && subset && font.subsets.includes(subset)) {
1299
1560
  let url = match[2]?.trim().replace(/^["']|["']$/g, ""), file = assets.files.find((entry) => entry.url === url);
1300
1561
  file && preloads.add(
1301
- ` preload("${ASSET_PREFIX}/_publishos/fonts/${file.sha256}.${file.format}", { as: "font", type: "font/${file.format}", crossOrigin: "anonymous" });`
1562
+ ` preload("${INTERNAL_PREFIX}/fonts/${file.sha256}.${file.format}", { as: "font", type: "font/${file.format}", crossOrigin: "anonymous" });`
1302
1563
  );
1303
1564
  }
1304
1565
  return {
@@ -1315,6 +1576,183 @@ export const ${exportName}: { className: string; variable: string; style: { font
1315
1576
  };
1316
1577
  }
1317
1578
 
1579
+ // src/lib/compiler/nextjs/mdx-icon-components.ts
1580
+ function mdxIconComponentsModule(pages) {
1581
+ let names = /* @__PURE__ */ new Map(), entries = [];
1582
+ for (let page of pages) {
1583
+ let bindings = readMdxIconImports(page.body);
1584
+ if (bindings.length === 0) continue;
1585
+ let properties = bindings.map(({ imported, local }) => {
1586
+ let identifier = names.get(imported);
1587
+ return identifier || (identifier = `ImportedIcon${names.size}`, names.set(imported, identifier)), `[${JSON.stringify(local)}]: ${identifier}`;
1588
+ });
1589
+ entries.push(`${JSON.stringify(page.path)}: { ${properties.join(", ")} }`);
1590
+ }
1591
+ return [
1592
+ 'import type { MDXComponents } from "@publish.os/nextjs/mdx/mdx";',
1593
+ ...names.size ? [
1594
+ `import { ${[...names].map(([name, identifier]) => `${name} as ${identifier}`).join(", ")} } from ${JSON.stringify(mdxIconPackage)};`
1595
+ ] : [],
1596
+ `export const importedMdxComponents: Record<string, MDXComponents> = {
1597
+ ${entries.join(`,
1598
+ `)}
1599
+ };
1600
+ `
1601
+ ].join(`
1602
+ `);
1603
+ }
1604
+
1605
+ // src/lib/runtime/metadata/canonical-url.ts
1606
+ function canonicalUrl(manifest, path, group) {
1607
+ let origin = (group && manifest.groups[group]?.origin) ?? manifest.site.origin;
1608
+ if (!origin) return path;
1609
+ try {
1610
+ return new URL(path, `${origin}/`).toString();
1611
+ } catch {
1612
+ return path;
1613
+ }
1614
+ }
1615
+ function navigationUrl(manifest, path, group) {
1616
+ let origin = manifest.groups[group]?.origin ?? manifest.site.origin;
1617
+ return !origin || origin === manifest.site.origin ? path : canonicalUrl(manifest, path, group);
1618
+ }
1619
+
1620
+ // src/route-not-found.ts
1621
+ var UNSAFE_PATH_CHARACTERS = /[\p{Cc}\p{Cs}\u200b-\u200f\u2028-\u202f\u2060-\u206f\ufeff]/u;
1622
+ var DEFAULT_MARKDOWN = serializeNotFoundMarkdown({
1623
+ recommendations: [],
1624
+ indexes: []
1625
+ });
1626
+ function publicRecoveryHref(href) {
1627
+ if (href.includes("\\") || UNSAFE_PATH_CHARACTERS.test(href))
1628
+ return;
1629
+ let relative5 = href.startsWith("/") && !href.startsWith("//");
1630
+ try {
1631
+ let url = new URL(
1632
+ href,
1633
+ relative5 ? "https://publishos.invalid" : void 0
1634
+ );
1635
+ return !["http:", "https:"].includes(url.protocol) || url.username || url.password ? void 0 : relative5 ? `${url.pathname}${url.search}${url.hash}` : url.href;
1636
+ } catch {
1637
+ return;
1638
+ }
1639
+ }
1640
+ function markdownText(value) {
1641
+ return value.replace(/\s+/gu, " ").replace(/[\p{Cc}\p{Cs}]/gu, "").trim().replace(/[\\`*_[\]{}<>|~]/gu, "\\$&");
1642
+ }
1643
+ function markdownLink(link) {
1644
+ let title = markdownText(link.title), href = publicRecoveryHref(link.href)?.replace(
1645
+ /[()`<>]/gu,
1646
+ (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`
1647
+ );
1648
+ if (!title || !href) return;
1649
+ let description = link.description ? markdownText(link.description) : void 0;
1650
+ return `- [${title}](${href})${description ? `: ${description}` : ""}`;
1651
+ }
1652
+ function serializeNotFoundMarkdown({
1653
+ recommendations,
1654
+ indexes
1655
+ }) {
1656
+ let sections = [
1657
+ { title: "Recommendations", links: recommendations },
1658
+ { title: "For crawlers and agents", links: indexes }
1659
+ ], lines = [
1660
+ "# 404 \u2014 Not Found",
1661
+ "",
1662
+ "This page does not exist. Please check the URL and try again.",
1663
+ ""
1664
+ ];
1665
+ for (let section of sections) {
1666
+ let links = section.links.map(markdownLink).filter((link) => link !== void 0);
1667
+ links.length && lines.push(`## ${section.title}`, "", ...links, "");
1668
+ }
1669
+ return lines.join(`
1670
+ `);
1671
+ }
1672
+
1673
+ // src/lib/compiler/nextjs/not-found-plan.ts
1674
+ 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
+ );
1679
+ }
1680
+ function createNotFoundScopes({
1681
+ manifest,
1682
+ outputs,
1683
+ rootOwnedGroups,
1684
+ markdownPaths = /* @__PURE__ */ new Set()
1685
+ }) {
1686
+ return [
1687
+ void 0,
1688
+ ...Object.values(manifest.groups).filter(
1689
+ (group) => group.key !== ROOT_CONTENT_GROUP_KEY
1690
+ )
1691
+ ].map((group) => {
1692
+ let path = group?.path ?? "/", origin = group?.origin ?? manifest.site.origin, rootOwned = !group || (rootOwnedGroups ? rootOwnedGroups.has(group.key) : origin === manifest.site.origin), groupKeys = new Set(group?.ancestry ?? []);
1693
+ group && groupKeys.add(group.key);
1694
+ let url = (route, key = group?.key) => canonicalUrl(manifest, route, key), recommendations = [];
1695
+ group && recommendations.push({
1696
+ title: `Back to ${group.title}`,
1697
+ href: url(group.path)
1698
+ });
1699
+ 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];
1702
+ first && first.path !== path && recommendations.push({
1703
+ title: first.title,
1704
+ href: url(first.path, first.group)
1705
+ }), rootOwned && !recommendations.some((link) => link.href === url("/")) && recommendations.push({ title: "Go home", href: url("/") });
1706
+ let available = outputs.filter((output) => {
1707
+ let scopeGroup = output.scopeGroup ?? output.group;
1708
+ return (output.group && manifest.groups[output.group]?.origin || manifest.site.origin) !== origin || !output.group && !rootOwned || scopeGroup && group && output.scope !== "site" && !groupKeys.has(scopeGroup) || scopeGroup && !group && output.scope !== "site" || output.scope === "self" && scopeGroup !== group?.key ? !1 : belongsToPath(path, outputDirectory(output.path));
1709
+ }), choose = (kind) => available.filter((output) => output.kind === kind).sort(
1710
+ (a, b) => +!!a.group - +!!b.group || +(b.sitemaps !== void 0) - +(a.sitemaps !== void 0) || (manifest.groups[b.group ?? ""]?.path.length ?? 0) - (manifest.groups[a.group ?? ""]?.path.length ?? 0)
1711
+ )[0], sitemap = choose("sitemap") ?? choose("sitemap-md"), llms = choose("llms"), full = choose("llms-full"), nearestLlms = available.filter((output) => output.kind === "llms").sort(
1712
+ (a, b) => outputDirectory(b.path).length - outputDirectory(a.path).length || (manifest.groups[b.group ?? ""]?.path.length ?? 0) - (manifest.groups[a.group ?? ""]?.path.length ?? 0)
1713
+ )[0], indexes = [
1714
+ sitemap && {
1715
+ title: sitemap.kind === "sitemap-md" ? "Markdown sitemap" : "Sitemap",
1716
+ href: url(sitemap.path, sitemap.group),
1717
+ kind: sitemap.kind
1718
+ },
1719
+ llms && {
1720
+ title: "llms.txt",
1721
+ href: url(llms.path, llms.group),
1722
+ kind: "llms"
1723
+ },
1724
+ full && {
1725
+ title: "llms-full.txt",
1726
+ href: url(full.path, full.group),
1727
+ kind: "llms-full"
1728
+ }
1729
+ ].filter(
1730
+ (link) => !!link
1731
+ ), agentLink = (link) => {
1732
+ let page = visible.find(
1733
+ (candidate) => url(candidate.path, candidate.group) === link.href
1734
+ ), markdownPath = page ? `${page.path}.md` : void 0;
1735
+ return markdownPath && markdownPaths.has(markdownPath) ? { ...link, href: url(markdownPath, page?.group) } : link;
1736
+ }, markdown = serializeNotFoundMarkdown({
1737
+ recommendations: recommendations.map(agentLink),
1738
+ indexes
1739
+ });
1740
+ return {
1741
+ id: group ? `g-${Buffer.from(group.key).toString("base64url")}` : "root",
1742
+ path,
1743
+ ...group ? { group: group.key } : {},
1744
+ ...origin ? { origin } : {},
1745
+ htmlPath: "/_not-found",
1746
+ recommendations,
1747
+ indexes,
1748
+ ...nearestLlms ? { describedBy: url(nearestLlms.path, nearestLlms.group) } : {},
1749
+ markdown
1750
+ };
1751
+ }).sort(
1752
+ (a, b) => b.path.length - a.path.length || +!!b.group - +!!a.group
1753
+ );
1754
+ }
1755
+
1318
1756
  // src/lib/compiler/nextjs/route-layout-planner.ts
1319
1757
  import { stat as stat2 } from "fs/promises";
1320
1758
  import { resolve } from "path";
@@ -1442,11 +1880,12 @@ function fontReadErrorDetails(error) {
1442
1880
  async function validateVariableFontFile({
1443
1881
  configuredPath,
1444
1882
  sourcePath,
1445
- weight
1883
+ weight,
1884
+ contents
1446
1885
  }) {
1447
1886
  let font;
1448
1887
  try {
1449
- font = await fontkit.open(sourcePath);
1888
+ font = contents ? fontkit.create(contents) : await fontkit.open(sourcePath);
1450
1889
  } catch (error) {
1451
1890
  let details = fontReadErrorDetails(error);
1452
1891
  throw details.reasonCode === "ENOENT" ? new AppError({
@@ -1762,7 +2201,7 @@ async function normalizedLogoDataUri(options) {
1762
2201
  };
1763
2202
  }
1764
2203
  async function logoDataUri(options) {
1765
- let output = options.faviconsAndLogos.outputs.find(
2204
+ let output = options.media.outputs.find(
1766
2205
  (candidate) => candidate.outputPath === options.entry.outputPath
1767
2206
  );
1768
2207
  if (output) {
@@ -1788,7 +2227,7 @@ async function resolveOgLogo(options) {
1788
2227
  if (!selected || !options.faviconsAndLogos) return;
1789
2228
  let logoData = await logoDataUri({
1790
2229
  entry: selected.entry,
1791
- faviconsAndLogos: options.faviconsAndLogos
2230
+ media: options.media
1792
2231
  });
1793
2232
  return {
1794
2233
  dataUri: logoData?.dataUri,
@@ -1871,8 +2310,7 @@ async function resolveOgFonts({
1871
2310
  path: file.path,
1872
2311
  sourceFile: "publish.os.config.json"
1873
2312
  });
1874
- let data = await readFile2(file.path).catch(() => {
1875
- });
2313
+ let data = file.data;
1876
2314
  if (!data || data.byteLength > MAX_OG_FONT_BYTES)
1877
2315
  return;
1878
2316
  let probe = fontProbe(data);
@@ -1911,9 +2349,6 @@ function fontProbe(data) {
1911
2349
  return;
1912
2350
  }
1913
2351
  }
1914
- function isRemoteImageAllowed(url, sources) {
1915
- return sources.some((source) => source.protocol !== "https" || url.protocol !== "https:" || source.hostname !== url.hostname ? !1 : source.pathname ? url.pathname.startsWith(source.pathname) : !0);
1916
- }
1917
2352
  function imageSource(image) {
1918
2353
  if (image)
1919
2354
  return typeof image == "string" ? { src: image } : image;
@@ -1947,10 +2382,10 @@ function resolveExplicitImage(options) {
1947
2382
  details: { image: source.src, routePath: options.routePath }
1948
2383
  });
1949
2384
  }
1950
- if (!isRemoteImageAllowed(url, options.mediaManifest.remote.images))
2385
+ if (url.protocol !== "https:")
1951
2386
  throw new AppError({
1952
2387
  code: "FRAMEWORK_OG_REMOTE_IMAGE_REJECTED",
1953
- message: `Remote OG image "${source.src}" for route "${options.routePath}" is not allowed by media.remote.images`,
2388
+ message: `Remote OG image "${source.src}" for route "${options.routePath}" must use HTTPS`,
1954
2389
  details: { image: source.src, routePath: options.routePath }
1955
2390
  });
1956
2391
  return { source: "remote", url: source.src, alt: source.alt };
@@ -2154,6 +2589,7 @@ async function buildOgManifest(options) {
2154
2589
  },
2155
2590
  logo: await resolveOgLogo({
2156
2591
  faviconsAndLogos: options.faviconsAndLogos,
2592
+ media: options.media,
2157
2593
  theme: colors.theme
2158
2594
  })
2159
2595
  }, groupGeneratedDefaults = Object.fromEntries(
@@ -2168,6 +2604,7 @@ async function buildOgManifest(options) {
2168
2604
  ...group?.origin ? { siteUrl: group.origin } : {},
2169
2605
  logo: await resolveOgLogo({
2170
2606
  faviconsAndLogos: options.faviconsAndLogos,
2607
+ media: options.media,
2171
2608
  theme: routeColors.theme
2172
2609
  })
2173
2610
  }
@@ -2625,24 +3062,7 @@ async function materializeAppInstallation({
2625
3062
  // src/lib/compiler/search/search-index.ts
2626
3063
  import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
2627
3064
  import { dirname as dirname4, resolve as resolve3 } from "path";
2628
-
2629
- // src/lib/runtime/metadata/canonical-url.ts
2630
- function canonicalUrl(manifest, path, group) {
2631
- let origin = (group && manifest.groups[group]?.origin) ?? manifest.site.origin;
2632
- if (!origin) return path;
2633
- try {
2634
- return new URL(path, `${origin}/`).toString();
2635
- } catch {
2636
- return path;
2637
- }
2638
- }
2639
- function navigationUrl(manifest, path, group) {
2640
- let origin = manifest.groups[group]?.origin ?? manifest.site.origin;
2641
- return !origin || origin === manifest.site.origin ? path : canonicalUrl(manifest, path, group);
2642
- }
2643
-
2644
- // src/lib/compiler/search/search-index.ts
2645
- var SEARCH_BUNDLE_DIR = PUBLIC_PAGEFIND_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...";
3065
+ 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...";
2646
3066
  function stringifyMetaValue(value) {
2647
3067
  if (typeof value == "string" && value.trim()) return value;
2648
3068
  if (typeof value == "number" || typeof value == "boolean")
@@ -2651,17 +3071,13 @@ function stringifyMetaValue(value) {
2651
3071
  function pageAuthors(page) {
2652
3072
  return page.authors.flatMap((author) => typeof author.name == "string" && author.name.trim() ? [author.name] : []);
2653
3073
  }
2654
- function pageContent(page) {
3074
+ function pageContent(page, conversions) {
2655
3075
  return [
2656
3076
  page.title,
2657
3077
  page.description,
2658
3078
  page.tags.join(" "),
2659
3079
  pageAuthors(page).join(" "),
2660
- convertMdxToMarkdown({
2661
- collectDiagnostics: !1,
2662
- source: page.body,
2663
- unknownComponentMode: "children"
2664
- }).text
3080
+ conversions.get(page.path)?.text ?? ""
2665
3081
  ].filter(Boolean).join(`
2666
3082
 
2667
3083
  `);
@@ -2689,9 +3105,9 @@ function pageMeta({
2689
3105
  function pagesToIndex(manifest) {
2690
3106
  return manifest.pages.filter((page) => page.status === "published").sort((a, b) => a.path.localeCompare(b.path));
2691
3107
  }
2692
- function createSearchRecords(manifest) {
3108
+ function createSearchRecords(manifest, conversions) {
2693
3109
  return pagesToIndex(manifest).map((page) => ({
2694
- content: pageContent(page),
3110
+ content: pageContent(page, conversions),
2695
3111
  filters: {
2696
3112
  author: pageAuthors(page),
2697
3113
  group: [page.group],
@@ -2708,11 +3124,12 @@ function createSearchRecords(manifest) {
2708
3124
  }));
2709
3125
  }
2710
3126
  async function writeSearchRecordsArtifact({
3127
+ conversions,
2711
3128
  manifest,
2712
3129
  outputDir
2713
3130
  }) {
2714
3131
  let artifact = {
2715
- records: createSearchRecords(manifest)
3132
+ records: createSearchRecords(manifest, conversions)
2716
3133
  }, filePath = resolve3(outputDir, SEARCH_RECORDS_PATH);
2717
3134
  await mkdir5(dirname4(filePath), { recursive: !0 }), await writeFile5(filePath, `${JSON.stringify(artifact, null, 2)}
2718
3135
  `, "utf-8");
@@ -2840,272 +3257,23 @@ try {
2840
3257
  `;
2841
3258
  }
2842
3259
 
2843
- // src/lib/compiler/theme/component-manifest.ts
2844
- import { readdir as readdir2, readFile as readFile4, stat as stat3 } from "fs/promises";
2845
- import { dirname as dirname5, extname, relative as relative2, resolve as resolve4, sep } from "path";
2846
- import ts2 from "typescript";
2847
- var componentFamilies = [
2848
- "dialog",
2849
- "drawer",
2850
- "menu",
2851
- "navigation-menu",
2852
- "popover",
2853
- "select",
2854
- "toast",
2855
- "tooltip"
2856
- ], SOURCE_EXTENSIONS = [".js", ".jsx", ".md", ".mdx", ".mjs", ".ts", ".tsx"], IMPORT_PATTERN = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s*)["']([^"']+)["']/g;
2857
- function sourceImports(content, path) {
2858
- if ([".md", ".mdx"].includes(extname(path)))
2859
- return {
2860
- dynamic: !1,
2861
- specifiers: [...content.matchAll(IMPORT_PATTERN)].flatMap(
2862
- (match) => match[1] ? [match[1]] : []
2863
- )
2864
- };
2865
- let source = ts2.createSourceFile(
2866
- path,
2867
- content,
2868
- ts2.ScriptTarget.Latest,
2869
- !1,
2870
- ts2.ScriptKind.TSX
2871
- ), specifiers = [], dynamic = !1, visit = (node) => {
2872
- if ((ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node)) && node.moduleSpecifier && ts2.isStringLiteral(node.moduleSpecifier))
2873
- specifiers.push(node.moduleSpecifier.text);
2874
- else if (ts2.isCallExpression(node) && node.expression.kind === ts2.SyntaxKind.ImportKeyword) {
2875
- let argument = node.arguments[0];
2876
- argument && ts2.isStringLiteral(argument) ? specifiers.push(argument.text) : dynamic = !0;
2877
- }
2878
- ts2.forEachChild(node, visit);
2879
- };
2880
- return visit(source), { dynamic, specifiers };
2881
- }
2882
- function portable(path) {
2883
- return path.split(sep).join("/");
2884
- }
2885
- async function fileExists2(path) {
2886
- return stat3(path).then((entry) => entry.isFile()).catch(() => !1);
2887
- }
2888
- async function sourceFiles(dir, prefix = "") {
2889
- let entries = await readdir2(dir, { withFileTypes: !0 }).catch(() => []), files = [];
2890
- for (let entry of entries) {
2891
- let relativePath = prefix ? `${prefix}/${entry.name}` : entry.name, path = resolve4(dir, entry.name);
2892
- if (entry.isDirectory()) {
2893
- if (relativePath === "components") continue;
2894
- files.push(...await sourceFiles(path, relativePath));
2895
- } else SOURCE_EXTENSIONS.includes(extname(entry.name)) && files.push(relativePath);
2896
- }
2897
- return files;
3260
+ // src/lib/compiler/theme/compile-theme-css.ts
3261
+ import { lstat as lstat2, stat as stat3 } from "fs/promises";
3262
+ import { dirname as dirname5, relative as relative2, resolve as resolve4, sep } from "path";
3263
+ import postcss2 from "postcss";
3264
+ import selectorParser from "postcss-selector-parser";
3265
+ import valueParser2 from "postcss-value-parser";
3266
+ var GROUP_CONDITIONAL_AT_RULES = /* @__PURE__ */ new Set(["container", "media", "supports"]), IMAGE_SET_FUNCTIONS2 = /* @__PURE__ */ new Set(["image-set", "-webkit-image-set"]);
3267
+ function fail(code, message, details) {
3268
+ throw new AppError({ code, message, details });
2898
3269
  }
2899
- async function resolveSourceFile(path) {
2900
- if (await fileExists2(path)) return path;
2901
- for (let extension of SOURCE_EXTENSIONS)
2902
- if (await fileExists2(`${path}${extension}`)) return `${path}${extension}`;
2903
- for (let extension of SOURCE_EXTENSIONS) {
2904
- let indexPath = resolve4(path, `index${extension}`);
2905
- if (await fileExists2(indexPath)) return indexPath;
3270
+ function hasRuleAncestor(rule) {
3271
+ let parent = rule.parent;
3272
+ for (; parent; ) {
3273
+ if (parent.type === "rule") return !0;
3274
+ parent = parent.parent;
2906
3275
  }
2907
- }
2908
- function resolveApplicationFile(application, path) {
2909
- let candidates = [
2910
- path,
2911
- ...SOURCE_EXTENSIONS.map((extension) => `${path}${extension}`),
2912
- ...SOURCE_EXTENSIONS.map((extension) => resolve4(path, `index${extension}`))
2913
- ];
2914
- for (let candidate of candidates) {
2915
- let file = application.get(candidate);
2916
- if (file) return [candidate, file];
2917
- }
2918
- }
2919
- function runtimeSubpath(specifier) {
2920
- let prefix = "@publish.os/nextjs";
2921
- if (specifier === prefix) return "index.js";
2922
- if (!specifier.startsWith(`${prefix}/`)) return;
2923
- let subpath = specifier.slice(prefix.length + 1);
2924
- return subpath === "mdx" ? "kernel/components/mdx/mdx.js" : subpath.startsWith("mdx/") ? `kernel/components/mdx/${subpath.slice(4)}.js` : `${subpath}.js`;
2925
- }
2926
- function familyForPath(path) {
2927
- let normalized = portable(path);
2928
- return componentFamilies.find(
2929
- (family) => new RegExp(`/components/ui/${family}\\.[cm]?[jt]sx?$`).test(normalized)
2930
- );
2931
- }
2932
- async function publicTokensForFamily({
2933
- family,
2934
- runtimeDistDir
2935
- }) {
2936
- let path = resolve4(
2937
- runtimeDistDir,
2938
- "../compiler-assets/nextjs/defaults/root/css/components",
2939
- `${family}.css`
2940
- ), css = await readFile4(path, "utf8").catch(() => "");
2941
- return [
2942
- ...new Set(
2943
- [...css.matchAll(new RegExp(`(--${family}-[a-z0-9-]+)\\s*:`, "g"))].map((match) => match[1]).filter((token) => !!token)
2944
- )
2945
- ].sort();
2946
- }
2947
- async function createComponentManifest({
2948
- applicationFiles,
2949
- runtimeDistDir,
2950
- sourceDir,
2951
- editableCheckout = !1
2952
- }) {
2953
- let application = new Map(
2954
- applicationFiles.map((file) => [
2955
- resolve4(dirname5(sourceDir), file.path),
2956
- file
2957
- ])
2958
- ), queue = [], visited = /* @__PURE__ */ new Set(), sources = /* @__PURE__ */ new Set(), sourceByKey = /* @__PURE__ */ new Map(), importsByKey = /* @__PURE__ */ new Map(), families = /* @__PURE__ */ new Set(), familyKeys = /* @__PURE__ */ new Map(), fallbackReasons = /* @__PURE__ */ new Set(), connect = (from, to) => {
2959
- if (!to) return;
2960
- let imports = importsByKey.get(from) ?? /* @__PURE__ */ new Set();
2961
- imports.add(to), importsByKey.set(from, imports);
2962
- };
2963
- if (editableCheckout)
2964
- for (let source of [
2965
- "../app",
2966
- "../source",
2967
- "../node_modules/@publish.os/nextjs/src",
2968
- "../node_modules/@publish.os/nextjs/compiler-assets/nextjs/defaults"
2969
- ])
2970
- sources.add(source);
2971
- for (let path of editableCheckout ? [] : await sourceFiles(sourceDir))
2972
- queue.push({ kind: "local", path: resolve4(sourceDir, path) });
2973
- for (let file of editableCheckout ? [] : applicationFiles)
2974
- queue.push({
2975
- content: file.content,
2976
- key: resolve4(dirname5(sourceDir), file.path),
2977
- kind: "application",
2978
- path: file.path
2979
- });
2980
- let enqueueRuntime = async (specifier) => {
2981
- let subpath = runtimeSubpath(specifier);
2982
- if (!subpath) return;
2983
- let path = resolve4(runtimeDistDir, subpath);
2984
- if (!await fileExists2(path)) {
2985
- fallbackReasons.add(`Missing runtime source for ${specifier}`);
2986
- return;
2987
- }
2988
- return queue.push({ kind: "runtime", path }), path;
2989
- };
2990
- for (; queue.length > 0; ) {
2991
- let entry = queue.shift();
2992
- if (!entry) continue;
2993
- let key = entry.kind === "application" ? entry.key : entry.path;
2994
- if (visited.has(key)) continue;
2995
- visited.add(key);
2996
- let content = entry.kind === "application" ? entry.content : await readFile4(entry.path, "utf8"), imports = sourceImports(content, key);
2997
- imports.dynamic && fallbackReasons.add(`Dynamic import in ${key}`);
2998
- let source = entry.kind === "runtime" ? `../node_modules/@publish.os/nextjs/dist/${portable(relative2(runtimeDistDir, entry.path))}` : entry.kind === "application" ? `../${portable(entry.path)}` : `../source/${portable(relative2(sourceDir, entry.path))}`;
2999
- sources.add(source), sourceByKey.set(key, source);
3000
- let family = familyForPath(key);
3001
- if (family) {
3002
- families.add(family);
3003
- let keys = familyKeys.get(family) ?? /* @__PURE__ */ new Set();
3004
- keys.add(key), familyKeys.set(family, keys);
3005
- }
3006
- for (let specifier of imports.specifiers) {
3007
- if (specifier.endsWith(".css")) continue;
3008
- if (specifier.startsWith("@publish.os/nextjs")) {
3009
- connect(key, await enqueueRuntime(specifier));
3010
- continue;
3011
- }
3012
- if (specifier.startsWith("@components/")) {
3013
- let componentPath = specifier.slice(12), local = await resolveSourceFile(
3014
- resolve4(sourceDir, "components", componentPath)
3015
- );
3016
- local ? (connect(key, local), queue.push({ kind: "local", path: local })) : connect(
3017
- key,
3018
- await enqueueRuntime(
3019
- `@publish.os/nextjs/kernel/components/${componentPath}`
3020
- )
3021
- );
3022
- continue;
3023
- }
3024
- if (specifier.startsWith("@source/")) {
3025
- let local = await resolveSourceFile(
3026
- resolve4(sourceDir, specifier.slice(8))
3027
- );
3028
- local ? (connect(key, local), queue.push({ kind: "local", path: local })) : fallbackReasons.add(`Missing source import ${specifier} in ${key}`);
3029
- continue;
3030
- }
3031
- if (!specifier.startsWith(".")) continue;
3032
- let base = entry.kind === "application" ? dirname5(entry.key) : dirname5(entry.path), unresolvedTarget = resolve4(base, specifier), planned = resolveApplicationFile(application, unresolvedTarget);
3033
- if (planned) {
3034
- connect(key, planned[0]), queue.push({
3035
- content: planned[1].content,
3036
- key: planned[0],
3037
- kind: "application",
3038
- path: planned[1].path
3039
- });
3040
- continue;
3041
- }
3042
- let target = await resolveSourceFile(unresolvedTarget);
3043
- target ? (connect(key, target), queue.push({
3044
- kind: target.startsWith(runtimeDistDir) ? "runtime" : "local",
3045
- path: target
3046
- })) : fallbackReasons.add(`Missing relative import ${specifier} in ${key}`);
3047
- }
3048
- }
3049
- let fallback = fallbackReasons.size > 0, activeFamilies = fallback || editableCheckout ? [...componentFamilies] : [...families].sort();
3050
- fallback && sources.add("../node_modules/@publish.os/nextjs/dist/**/*.js");
3051
- let componentEntries = await Promise.all(
3052
- activeFamilies.map(async (family) => {
3053
- let familySources = /* @__PURE__ */ new Set();
3054
- if (editableCheckout)
3055
- for (let source of sources) familySources.add(source);
3056
- let pending = [...familyKeys.get(family) ?? []], traced = /* @__PURE__ */ new Set();
3057
- for (; pending.length > 0; ) {
3058
- let key = pending.pop();
3059
- if (!key || traced.has(key)) continue;
3060
- traced.add(key);
3061
- let source = sourceByKey.get(key);
3062
- source && familySources.add(source), pending.push(...importsByKey.get(key) ?? []);
3063
- }
3064
- return fallback && familySources.add("../node_modules/@publish.os/nextjs/dist/**/*.js"), [
3065
- family,
3066
- {
3067
- optionalCss: `./css/components/${family}.css`,
3068
- publicTokenPrefix: `--${family}-`,
3069
- publicTokens: await publicTokensForFamily({
3070
- family,
3071
- runtimeDistDir
3072
- }),
3073
- source: `@publish.os/nextjs/kernel/components/ui/${family}`,
3074
- sources: [...familySources].sort()
3075
- }
3076
- ];
3077
- })
3078
- );
3079
- return {
3080
- components: Object.fromEntries(componentEntries),
3081
- fallback,
3082
- fallbackReasons: [...fallbackReasons].sort(),
3083
- families: activeFamilies,
3084
- sources: [...sources].sort()
3085
- };
3086
- }
3087
-
3088
- // src/lib/compiler/theme/create-theme-build-plan.ts
3089
- import { readFile as readFile5, stat as stat5 } from "fs/promises";
3090
- import { dirname as dirname7, resolve as resolve6 } from "path";
3091
-
3092
- // src/lib/compiler/theme/compile-theme-css.ts
3093
- import { stat as stat4 } from "fs/promises";
3094
- import { dirname as dirname6, relative as relative3, resolve as resolve5, sep as sep2 } from "path";
3095
- import postcss from "postcss";
3096
- import selectorParser from "postcss-selector-parser";
3097
- import valueParser from "postcss-value-parser";
3098
- var GROUP_CONDITIONAL_AT_RULES = /* @__PURE__ */ new Set(["container", "media", "supports"]), IMAGE_SET_FUNCTIONS = /* @__PURE__ */ new Set(["image-set", "-webkit-image-set"]);
3099
- function fail(code, message, details) {
3100
- throw new AppError({ code, message, details });
3101
- }
3102
- function hasRuleAncestor(rule) {
3103
- let parent = rule.parent;
3104
- for (; parent; ) {
3105
- if (parent.type === "rule") return !0;
3106
- parent = parent.parent;
3107
- }
3108
- return !1;
3276
+ return !1;
3109
3277
  }
3110
3278
  function rejectGlobalSelector(selector, sourcePath) {
3111
3279
  let globalElement;
@@ -3133,7 +3301,7 @@ function scopeGroupThemeCss({
3133
3301
  scopeSelectors,
3134
3302
  sourcePath
3135
3303
  }) {
3136
- let root = postcss.parse(css, { from: sourcePath });
3304
+ let root = postcss2.parse(css, { from: sourcePath });
3137
3305
  return root.walkAtRules((atRule) => {
3138
3306
  GROUP_CONDITIONAL_AT_RULES.has(atRule.name.toLowerCase()) || fail(
3139
3307
  "FRAMEWORK_GROUP_THEME_GLOBAL_AT_RULE",
@@ -3163,14 +3331,146 @@ function urlKind(value) {
3163
3331
  let trimmed = value.trim();
3164
3332
  return trimmed.startsWith("#") ? "fragment" : trimmed.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(trimmed) ? "unsafe" : trimmed.startsWith("/") ? "root" : "relative";
3165
3333
  }
3166
- async function fileExists3(path) {
3167
- return stat4(path).then((entry) => entry.isFile()).catch(() => !1);
3334
+ async function fileExists2(path) {
3335
+ return stat3(path).then((entry) => entry.isFile()).catch(() => !1);
3168
3336
  }
3169
3337
  function portablePath(path) {
3170
- return path.split(sep2).join("/");
3338
+ return path.split(sep).join("/");
3339
+ }
3340
+ var FONT_EXTENSIONS = /* @__PURE__ */ new Set(["woff", "woff2", "ttf", "otf"]);
3341
+ function validFontResource(contents, extension) {
3342
+ if (extension === "woff" || extension === "woff2") {
3343
+ let signature2 = extension === "woff" ? "wOFF" : "wOF2", headerBytes = extension === "woff" ? 44 : 48;
3344
+ return contents.length >= headerBytes && contents.toString("ascii", 0, 4) === signature2 && contents.readUInt32BE(8) === contents.length && contents.readUInt16BE(12) > 0;
3345
+ }
3346
+ if (contents.length < 12) return !1;
3347
+ let signature = contents.toString("ascii", 0, 4), truetype = contents.readUInt32BE(0) === 65536;
3348
+ if (extension === "ttf" && signature !== "true" && !truetype || extension === "otf" && signature !== "OTTO" && !truetype) return !1;
3349
+ let tables = contents.readUInt16BE(4);
3350
+ if (tables === 0 || tables > 4096 || 12 + tables * 16 > contents.length)
3351
+ return !1;
3352
+ for (let index = 0; index < tables; index += 1) {
3353
+ let table = 12 + index * 16, offset = contents.readUInt32BE(table + 8), length = contents.readUInt32BE(table + 12);
3354
+ if (offset > contents.length || length > contents.length - offset)
3355
+ return !1;
3356
+ }
3357
+ return !0;
3358
+ }
3359
+ function createThemeResourceAdmission({
3360
+ media,
3361
+ publishOsDir
3362
+ }) {
3363
+ let limits = mediaAssetBudgets(), baseBytes = media?.manifest.totals.bytes ?? 0, baseCount = media?.manifest.totals.count ?? 0, admitted = /* @__PURE__ */ new Map(), mediaPaths = /* @__PURE__ */ new Map();
3364
+ for (let entry of Object.values(media?.manifest.assets ?? {})) {
3365
+ let sourcePrefix = "publish.os/";
3366
+ entry.sourcePath.startsWith(sourcePrefix) && mediaPaths.set(
3367
+ resolve4(publishOsDir, entry.sourcePath.slice(sourcePrefix.length)),
3368
+ entry.outputPath
3369
+ );
3370
+ }
3371
+ let additionalBytes = 0;
3372
+ return {
3373
+ assets: admitted,
3374
+ async admit({
3375
+ filePath,
3376
+ relativePath,
3377
+ sourcePath
3378
+ }) {
3379
+ let existingMediaPath = mediaPaths.get(sourcePath);
3380
+ if (existingMediaPath)
3381
+ return { urlPath: `/${existingMediaPath.replace(/^\/+/, "")}` };
3382
+ let existing = admitted.get(sourcePath);
3383
+ if (existing)
3384
+ return {
3385
+ asset: existing,
3386
+ urlPath: themeResourceUrlPath(existing.outputPath)
3387
+ };
3388
+ let limit = themeResourceByteLimit(relativePath);
3389
+ if (limit === void 0)
3390
+ throw new AppError({
3391
+ code: "FRAMEWORK_MEDIA_TYPE_REJECTED",
3392
+ message: `${relativePath} is not an allowed theme resource type`,
3393
+ details: { path: relativePath }
3394
+ });
3395
+ let entry = await lstat2(sourcePath);
3396
+ if (!entry.isFile() || entry.isSymbolicLink())
3397
+ throw new AppError({
3398
+ code: "FRAMEWORK_SOURCE_DEPENDENCY_NOT_FILE",
3399
+ message: "A theme resource must be a regular file.",
3400
+ details: { file: filePath, target: relativePath }
3401
+ });
3402
+ if (entry.size > limit)
3403
+ throw mediaFileTooLarge({
3404
+ relativePath,
3405
+ bytes: entry.size,
3406
+ limit
3407
+ });
3408
+ let remainingBytes = limits.maxTotalBytes - baseBytes - additionalBytes, reservedTotalBytes = baseBytes + additionalBytes + entry.size;
3409
+ if (reservedTotalBytes > limits.maxTotalBytes)
3410
+ throw new AppError({
3411
+ code: "FRAMEWORK_MEDIA_TOTAL_TOO_LARGE",
3412
+ message: `Theme resources and publish.os/assets total ${reservedTotalBytes} bytes, exceeding the limit of ${limits.maxTotalBytes}`,
3413
+ details: { bytes: reservedTotalBytes, limit: limits.maxTotalBytes }
3414
+ });
3415
+ let totalCount = baseCount + admitted.size + 1;
3416
+ if (totalCount > limits.maxFileCount)
3417
+ throw new AppError({
3418
+ code: "FRAMEWORK_MEDIA_FILE_COUNT_EXCEEDED",
3419
+ message: `Theme resources and publish.os/assets contain more than ${limits.maxFileCount} files, exceeding the limit of ${limits.maxFileCount}`,
3420
+ details: { count: totalCount, limit: limits.maxFileCount }
3421
+ });
3422
+ let contents = await readBoundedMediaFile({
3423
+ path: sourcePath,
3424
+ relativePath,
3425
+ limit: Math.min(limit, remainingBytes),
3426
+ expectedIdentity: { dev: entry.dev, ino: entry.ino }
3427
+ });
3428
+ if (contents.byteLength > limit)
3429
+ throw mediaFileTooLarge({
3430
+ relativePath,
3431
+ bytes: contents.byteLength,
3432
+ limit
3433
+ });
3434
+ if (contents.byteLength > remainingBytes) {
3435
+ let totalBytes = baseBytes + additionalBytes + contents.byteLength;
3436
+ throw new AppError({
3437
+ code: "FRAMEWORK_MEDIA_TOTAL_TOO_LARGE",
3438
+ message: `Theme resources and publish.os/assets total ${totalBytes} bytes, exceeding the limit of ${limits.maxTotalBytes}`,
3439
+ details: { bytes: totalBytes, limit: limits.maxTotalBytes }
3440
+ });
3441
+ }
3442
+ let extension = relativePath.split(".").at(-1)?.toLowerCase() ?? "", admittedContents;
3443
+ if (FONT_EXTENSIONS.has(extension)) {
3444
+ if (!validFontResource(contents, extension))
3445
+ throw new AppError({
3446
+ code: "FRAMEWORK_THEME_FONT_INVALID",
3447
+ message: `${relativePath} is not a valid ${extension.toUpperCase()} font`,
3448
+ details: { path: relativePath }
3449
+ });
3450
+ admittedContents = contents;
3451
+ } else
3452
+ admittedContents = (await admitThemeImageResource({ contents, relativePath })).contents;
3453
+ let admittedTotalBytes = baseBytes + additionalBytes + admittedContents.byteLength;
3454
+ if (admittedTotalBytes > limits.maxTotalBytes)
3455
+ throw new AppError({
3456
+ code: "FRAMEWORK_MEDIA_TOTAL_TOO_LARGE",
3457
+ message: `Theme resources and publish.os/assets total ${admittedTotalBytes} bytes, exceeding the limit of ${limits.maxTotalBytes}`,
3458
+ details: { bytes: admittedTotalBytes, limit: limits.maxTotalBytes }
3459
+ });
3460
+ let projectPath = portablePath(relative2(publishOsDir, sourcePath)), asset = {
3461
+ contents: admittedContents,
3462
+ outputPath: `${PUBLIC_INTERNAL_DIRECTORY}/themes/${projectPath}`,
3463
+ sourcePath
3464
+ };
3465
+ return admitted.set(sourcePath, asset), additionalBytes += admittedContents.byteLength, {
3466
+ asset,
3467
+ urlPath: themeResourceUrlPath(asset.outputPath)
3468
+ };
3469
+ }
3470
+ };
3171
3471
  }
3172
3472
  function cssUrlReferences(value) {
3173
- let parsed = valueParser(value), references = [];
3473
+ let parsed = valueParser2(value), references = [];
3174
3474
  return parsed.walk((node) => {
3175
3475
  if (node.type !== "function") return;
3176
3476
  let functionName = node.value.toLowerCase();
@@ -3184,10 +3484,10 @@ function cssUrlReferences(value) {
3184
3484
  valueNode.value = nextValue;
3185
3485
  }
3186
3486
  } : {},
3187
- value: valueNode?.value ?? valueParser.stringify(node.nodes).trim()
3487
+ value: valueNode?.value ?? valueParser2.stringify(node.nodes).trim()
3188
3488
  }), !1;
3189
3489
  }
3190
- if (IMAGE_SET_FUNCTIONS.has(functionName))
3490
+ if (IMAGE_SET_FUNCTIONS2.has(functionName))
3191
3491
  for (let child of node.nodes)
3192
3492
  child.type === "string" && references.push({
3193
3493
  set: (nextValue) => {
@@ -3204,18 +3504,23 @@ function pathAndSuffix(url) {
3204
3504
  suffix: url.slice(suffixIndex)
3205
3505
  };
3206
3506
  }
3507
+ function themeResourceUrlPath(outputPath) {
3508
+ return `/${outputPath.replace(/^\/+/, "").split("/").map((segment) => encodeURIComponent(segment)).join("/")}`;
3509
+ }
3207
3510
  async function rewriteThemeCssUrls({
3208
3511
  css,
3209
3512
  filePath,
3210
3513
  publishOsDir,
3514
+ media,
3515
+ resourceAdmission,
3211
3516
  scopeRoot
3212
3517
  }) {
3213
- let root = postcss.parse(css, { from: filePath }), assets = /* @__PURE__ */ new Map(), declarations = [];
3518
+ let admission = resourceAdmission ?? createThemeResourceAdmission({ media, publishOsDir }), root = postcss2.parse(css, { from: filePath }), assets = /* @__PURE__ */ new Map(), warnings = /* @__PURE__ */ new Map(), declarations = [];
3214
3519
  root.walkDecls((declaration) => {
3215
3520
  declarations.push(declaration);
3216
3521
  });
3217
3522
  for (let declaration of declarations) {
3218
- let { parsed, references } = cssUrlReferences(declaration.value);
3523
+ let { parsed, references } = cssUrlReferences(declaration.value), declarationAssets = /* @__PURE__ */ new Map(), preparedReferences = [], ignoredDeclaration = !1;
3219
3524
  for (let reference of references) {
3220
3525
  let url = decodeCssPath(reference.value);
3221
3526
  reference.set || fail(
@@ -3227,33 +3532,313 @@ async function rewriteThemeCssUrls({
3227
3532
  if (kind === "unsafe" && fail(
3228
3533
  "FRAMEWORK_THEME_URL_REJECTED",
3229
3534
  "Theme CSS contains an unsafe URL.",
3230
- {
3231
- source: filePath,
3232
- url
3233
- }
3535
+ { source: filePath, url }
3234
3536
  ), kind !== "relative") continue;
3235
- let { path, suffix } = pathAndSuffix(url), sourcePath = resolve5(dirname6(filePath), path);
3537
+ let { path, suffix } = pathAndSuffix(url), sourcePath = resolve4(dirname5(filePath), path);
3236
3538
  isSameOrDescendant(scopeRoot, sourcePath) || fail(
3237
3539
  "FRAMEWORK_THEME_URL_ESCAPE",
3238
3540
  "Theme CSS assets cannot escape their theme scope.",
3239
3541
  { source: filePath, url }
3240
- ), await fileExists3(sourcePath) || fail(
3542
+ );
3543
+ let projectPath = portablePath(relative2(publishOsDir, sourcePath));
3544
+ await fileExists2(sourcePath) || fail(
3241
3545
  "FRAMEWORK_THEME_ASSET_MISSING",
3242
3546
  "A theme CSS asset does not exist.",
3243
- {
3244
- source: filePath,
3245
- url
3246
- }
3247
- );
3248
- let projectPath = portablePath(relative3(publishOsDir, sourcePath)), outputPath = `${PUBLIC_ASSET_DIRECTORY}/__publishos/theme-assets/${projectPath}`;
3249
- assets.set(sourcePath, { outputPath, sourcePath }), reference.set(`/${encodeURI(outputPath)}${suffix}`);
3547
+ { source: filePath, url }
3548
+ ), await assertSourceDependency({
3549
+ publishOsDir,
3550
+ path: sourcePath,
3551
+ sourceFile: filePath
3552
+ });
3553
+ let ignoredDocument = ignoredMediaDocumentWarning(projectPath);
3554
+ if (ignoredDocument) {
3555
+ warnings.set(projectPath, { ...ignoredDocument, level: "warning" }), ignoredDeclaration = !0;
3556
+ continue;
3557
+ }
3558
+ if (isHiddenMediaPath(projectPath)) {
3559
+ warnings.set(projectPath, {
3560
+ ...ignoredMediaPathWarning(projectPath),
3561
+ level: "warning"
3562
+ }), ignoredDeclaration = !0;
3563
+ continue;
3564
+ }
3565
+ preparedReferences.push({
3566
+ set: reference.set,
3567
+ suffix,
3568
+ sourcePath,
3569
+ projectPath
3570
+ });
3571
+ }
3572
+ if (ignoredDeclaration) {
3573
+ declaration.remove();
3574
+ continue;
3575
+ }
3576
+ for (let { set, suffix, sourcePath, projectPath } of preparedReferences) {
3577
+ let admitted = await admission.admit({
3578
+ filePath,
3579
+ relativePath: projectPath,
3580
+ sourcePath
3581
+ });
3582
+ admitted.asset && declarationAssets.set(sourcePath, admitted.asset), set(`${admitted.urlPath}${suffix}`);
3583
+ }
3584
+ for (let [path, asset] of declarationAssets) assets.set(path, asset);
3585
+ declaration.value = parsed.toString();
3586
+ }
3587
+ return {
3588
+ assets: [...assets.values()],
3589
+ css: root.toString(),
3590
+ warnings: [...warnings.values()]
3591
+ };
3592
+ }
3593
+
3594
+ // src/lib/compiler/theme/component-manifest.ts
3595
+ import { readdir as readdir2, readFile as readFile4, stat as stat4 } from "fs/promises";
3596
+ import { dirname as dirname6, extname, relative as relative3, resolve as resolve5, sep as sep2 } from "path";
3597
+ import ts2 from "typescript";
3598
+ var componentFamilies = [
3599
+ "dialog",
3600
+ "drawer",
3601
+ "menu",
3602
+ "navigation-menu",
3603
+ "popover",
3604
+ "select",
3605
+ "toast",
3606
+ "tooltip"
3607
+ ], SOURCE_EXTENSIONS = [".js", ".jsx", ".md", ".mdx", ".mjs", ".ts", ".tsx"], IMPORT_PATTERN = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s*)["']([^"']+)["']/g;
3608
+ function sourceImports(content, path) {
3609
+ if ([".md", ".mdx"].includes(extname(path)))
3610
+ return {
3611
+ dynamic: !1,
3612
+ specifiers: [...content.matchAll(IMPORT_PATTERN)].flatMap(
3613
+ (match) => match[1] ? [match[1]] : []
3614
+ )
3615
+ };
3616
+ let source = ts2.createSourceFile(
3617
+ path,
3618
+ content,
3619
+ ts2.ScriptTarget.Latest,
3620
+ !1,
3621
+ ts2.ScriptKind.TSX
3622
+ ), specifiers = [], dynamic = !1, visit = (node) => {
3623
+ if ((ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node)) && node.moduleSpecifier && ts2.isStringLiteral(node.moduleSpecifier))
3624
+ specifiers.push(node.moduleSpecifier.text);
3625
+ else if (ts2.isCallExpression(node) && node.expression.kind === ts2.SyntaxKind.ImportKeyword) {
3626
+ let argument = node.arguments[0];
3627
+ argument && ts2.isStringLiteral(argument) ? specifiers.push(argument.text) : dynamic = !0;
3628
+ }
3629
+ ts2.forEachChild(node, visit);
3630
+ };
3631
+ return visit(source), { dynamic, specifiers };
3632
+ }
3633
+ function portable(path) {
3634
+ return path.split(sep2).join("/");
3635
+ }
3636
+ async function fileExists3(path) {
3637
+ return stat4(path).then((entry) => entry.isFile()).catch(() => !1);
3638
+ }
3639
+ async function sourceFiles(dir, prefix = "") {
3640
+ let entries = await readdir2(dir, { withFileTypes: !0 }).catch(() => []), files = [];
3641
+ for (let entry of entries) {
3642
+ let relativePath = prefix ? `${prefix}/${entry.name}` : entry.name, path = resolve5(dir, entry.name);
3643
+ if (entry.isDirectory()) {
3644
+ if (relativePath === "components") continue;
3645
+ files.push(...await sourceFiles(path, relativePath));
3646
+ } else SOURCE_EXTENSIONS.includes(extname(entry.name)) && files.push(relativePath);
3647
+ }
3648
+ return files;
3649
+ }
3650
+ async function resolveSourceFile(path) {
3651
+ if (await fileExists3(path)) return path;
3652
+ for (let extension of SOURCE_EXTENSIONS)
3653
+ if (await fileExists3(`${path}${extension}`)) return `${path}${extension}`;
3654
+ for (let extension of SOURCE_EXTENSIONS) {
3655
+ let indexPath = resolve5(path, `index${extension}`);
3656
+ if (await fileExists3(indexPath)) return indexPath;
3657
+ }
3658
+ }
3659
+ function resolveApplicationFile(application, path) {
3660
+ let candidates = [
3661
+ path,
3662
+ ...SOURCE_EXTENSIONS.map((extension) => `${path}${extension}`),
3663
+ ...SOURCE_EXTENSIONS.map((extension) => resolve5(path, `index${extension}`))
3664
+ ];
3665
+ for (let candidate of candidates) {
3666
+ let file = application.get(candidate);
3667
+ if (file) return [candidate, file];
3668
+ }
3669
+ }
3670
+ function runtimeSubpath(specifier) {
3671
+ let prefix = "@publish.os/nextjs";
3672
+ if (specifier === prefix) return "index.js";
3673
+ if (!specifier.startsWith(`${prefix}/`)) return;
3674
+ let requestedSubpath = specifier.slice(prefix.length + 1), subpath = nextjs_runtime_surface_default.entryAliases[requestedSubpath] ?? requestedSubpath;
3675
+ return subpath === "mdx" ? "kernel/components/mdx/mdx.js" : subpath.startsWith("mdx/") ? `kernel/components/mdx/${subpath.slice(4)}.js` : `${subpath}.js`;
3676
+ }
3677
+ function familyForPath(path) {
3678
+ let normalized = portable(path);
3679
+ return componentFamilies.find(
3680
+ (family) => new RegExp(`/components/ui/${family}\\.[cm]?[jt]sx?$`).test(normalized)
3681
+ );
3682
+ }
3683
+ async function publicTokensForFamily({
3684
+ family,
3685
+ runtimeDistDir
3686
+ }) {
3687
+ let path = resolve5(
3688
+ runtimeDistDir,
3689
+ "../compiler-assets/nextjs/defaults/root/css/components",
3690
+ `${family}.css`
3691
+ ), css = await readFile4(path, "utf8").catch(() => "");
3692
+ return [
3693
+ ...new Set(
3694
+ [...css.matchAll(new RegExp(`(--${family}-[a-z0-9-]+)\\s*:`, "g"))].map((match) => match[1]).filter((token) => !!token)
3695
+ )
3696
+ ].sort();
3697
+ }
3698
+ async function createComponentManifest({
3699
+ applicationFiles,
3700
+ runtimeDistDir,
3701
+ sourceDir,
3702
+ editableCheckout = !1
3703
+ }) {
3704
+ let application = new Map(
3705
+ applicationFiles.map((file) => [
3706
+ resolve5(dirname6(sourceDir), file.path),
3707
+ file
3708
+ ])
3709
+ ), queue = [], visited = /* @__PURE__ */ new Set(), sources = /* @__PURE__ */ new Set(), sourceByKey = /* @__PURE__ */ new Map(), importsByKey = /* @__PURE__ */ new Map(), families = /* @__PURE__ */ new Set(), familyKeys = /* @__PURE__ */ new Map(), fallbackReasons = /* @__PURE__ */ new Set(), connect = (from, to) => {
3710
+ if (!to) return;
3711
+ let imports = importsByKey.get(from) ?? /* @__PURE__ */ new Set();
3712
+ imports.add(to), importsByKey.set(from, imports);
3713
+ };
3714
+ if (editableCheckout)
3715
+ for (let source of [
3716
+ "../app",
3717
+ "../source",
3718
+ "../node_modules/@publish.os/nextjs/src",
3719
+ "../node_modules/@publish.os/nextjs/compiler-assets/nextjs/defaults"
3720
+ ])
3721
+ sources.add(source);
3722
+ for (let path of editableCheckout ? [] : await sourceFiles(sourceDir))
3723
+ queue.push({ kind: "local", path: resolve5(sourceDir, path) });
3724
+ for (let file of editableCheckout ? [] : applicationFiles)
3725
+ queue.push({
3726
+ content: file.content,
3727
+ key: resolve5(dirname6(sourceDir), file.path),
3728
+ kind: "application",
3729
+ path: file.path
3730
+ });
3731
+ let enqueueRuntime = async (specifier) => {
3732
+ let subpath = runtimeSubpath(specifier);
3733
+ if (!subpath) return;
3734
+ let path = resolve5(runtimeDistDir, subpath);
3735
+ if (!await fileExists3(path)) {
3736
+ fallbackReasons.add(`Missing runtime source for ${specifier}`);
3737
+ return;
3738
+ }
3739
+ return queue.push({ kind: "runtime", path }), path;
3740
+ };
3741
+ for (; queue.length > 0; ) {
3742
+ let entry = queue.shift();
3743
+ if (!entry) continue;
3744
+ let key = entry.kind === "application" ? entry.key : entry.path;
3745
+ if (visited.has(key)) continue;
3746
+ visited.add(key);
3747
+ let content = entry.kind === "application" ? entry.content : await readFile4(entry.path, "utf8"), imports = sourceImports(content, key);
3748
+ imports.dynamic && fallbackReasons.add(`Dynamic import in ${key}`);
3749
+ let source = entry.kind === "runtime" ? `../node_modules/@publish.os/nextjs/dist/${portable(relative3(runtimeDistDir, entry.path))}` : entry.kind === "application" ? `../${portable(entry.path)}` : `../source/${portable(relative3(sourceDir, entry.path))}`;
3750
+ sources.add(source), sourceByKey.set(key, source);
3751
+ let family = familyForPath(key);
3752
+ if (family) {
3753
+ families.add(family);
3754
+ let keys = familyKeys.get(family) ?? /* @__PURE__ */ new Set();
3755
+ keys.add(key), familyKeys.set(family, keys);
3756
+ }
3757
+ for (let specifier of imports.specifiers) {
3758
+ if (specifier.endsWith(".css")) continue;
3759
+ if (specifier.startsWith("@publish.os/nextjs")) {
3760
+ connect(key, await enqueueRuntime(specifier));
3761
+ continue;
3762
+ }
3763
+ if (specifier.startsWith("@components/")) {
3764
+ let componentPath = specifier.slice(12), local = await resolveSourceFile(
3765
+ resolve5(sourceDir, "components", componentPath)
3766
+ );
3767
+ local ? (connect(key, local), queue.push({ kind: "local", path: local })) : connect(
3768
+ key,
3769
+ await enqueueRuntime(
3770
+ `@publish.os/nextjs/kernel/components/${componentPath}`
3771
+ )
3772
+ );
3773
+ continue;
3774
+ }
3775
+ if (specifier.startsWith("@source/")) {
3776
+ let local = await resolveSourceFile(
3777
+ resolve5(sourceDir, specifier.slice(8))
3778
+ );
3779
+ local ? (connect(key, local), queue.push({ kind: "local", path: local })) : fallbackReasons.add(`Missing source import ${specifier} in ${key}`);
3780
+ continue;
3781
+ }
3782
+ if (!specifier.startsWith(".")) continue;
3783
+ let base = entry.kind === "application" ? dirname6(entry.key) : dirname6(entry.path), unresolvedTarget = resolve5(base, specifier), planned = resolveApplicationFile(application, unresolvedTarget);
3784
+ if (planned) {
3785
+ connect(key, planned[0]), queue.push({
3786
+ content: planned[1].content,
3787
+ key: planned[0],
3788
+ kind: "application",
3789
+ path: planned[1].path
3790
+ });
3791
+ continue;
3792
+ }
3793
+ let target = await resolveSourceFile(unresolvedTarget);
3794
+ target ? (connect(key, target), queue.push({
3795
+ kind: target.startsWith(runtimeDistDir) ? "runtime" : "local",
3796
+ path: target
3797
+ })) : fallbackReasons.add(`Missing relative import ${specifier} in ${key}`);
3250
3798
  }
3251
- declaration.value = parsed.toString();
3252
3799
  }
3253
- return { assets: [...assets.values()], css: root.toString() };
3800
+ let fallback = fallbackReasons.size > 0, activeFamilies = fallback || editableCheckout ? [...componentFamilies] : [...families].sort();
3801
+ fallback && sources.add("../node_modules/@publish.os/nextjs/dist/**/*.js");
3802
+ let componentEntries = await Promise.all(
3803
+ activeFamilies.map(async (family) => {
3804
+ let familySources = /* @__PURE__ */ new Set();
3805
+ if (editableCheckout)
3806
+ for (let source of sources) familySources.add(source);
3807
+ let pending = [...familyKeys.get(family) ?? []], traced = /* @__PURE__ */ new Set();
3808
+ for (; pending.length > 0; ) {
3809
+ let key = pending.pop();
3810
+ if (!key || traced.has(key)) continue;
3811
+ traced.add(key);
3812
+ let source = sourceByKey.get(key);
3813
+ source && familySources.add(source), pending.push(...importsByKey.get(key) ?? []);
3814
+ }
3815
+ return fallback && familySources.add("../node_modules/@publish.os/nextjs/dist/**/*.js"), [
3816
+ family,
3817
+ {
3818
+ optionalCss: `./css/components/${family}.css`,
3819
+ publicTokenPrefix: `--${family}-`,
3820
+ publicTokens: await publicTokensForFamily({
3821
+ family,
3822
+ runtimeDistDir
3823
+ }),
3824
+ source: `@publish.os/nextjs/kernel/components/ui/${family}`,
3825
+ sources: [...familySources].sort()
3826
+ }
3827
+ ];
3828
+ })
3829
+ );
3830
+ return {
3831
+ components: Object.fromEntries(componentEntries),
3832
+ fallback,
3833
+ fallbackReasons: [...fallbackReasons].sort(),
3834
+ families: activeFamilies,
3835
+ sources: [...sources].sort()
3836
+ };
3254
3837
  }
3255
3838
 
3256
3839
  // src/lib/compiler/theme/create-theme-build-plan.ts
3840
+ import { readFile as readFile5, stat as stat5 } from "fs/promises";
3841
+ import { dirname as dirname7, resolve as resolve6 } from "path";
3257
3842
  var GROUP_KEY_PATTERN = /^[a-z0-9/-]+$/, installedFramework = readInstalledFrameworkLocation(), THEME_DEFAULTS_PATH = resolve6(
3258
3843
  installedFramework.packageRoot,
3259
3844
  "compiler-assets/nextjs/shims/theme-defaults.css"
@@ -3288,6 +3873,8 @@ async function inlineLocalCssImports({
3288
3873
  rootDir,
3289
3874
  publishOsDir,
3290
3875
  assets,
3876
+ resourceAdmission,
3877
+ onWarning,
3291
3878
  seen = /* @__PURE__ */ new Set()
3292
3879
  }) {
3293
3880
  seen.has(filePath) && fail2("FRAMEWORK_THEME_IMPORT_CYCLE", "Theme CSS imports contain a cycle.", {
@@ -3301,9 +3888,16 @@ async function inlineLocalCssImports({
3301
3888
  css: await readFile5(filePath, "utf-8"),
3302
3889
  filePath,
3303
3890
  publishOsDir,
3891
+ resourceAdmission,
3304
3892
  scopeRoot: rootDir
3305
3893
  });
3306
3894
  for (let asset of rewritten.assets) assets.set(asset.sourcePath, asset);
3895
+ for (let warning of rewritten.warnings)
3896
+ onWarning?.({
3897
+ code: warning.code,
3898
+ message: warning.message,
3899
+ ...typeof warning.details?.file == "string" ? { file: warning.details.file } : {}
3900
+ });
3307
3901
  assertNoThemeModeCssVariables(rewritten.css, filePath);
3308
3902
  let imports = cssImports({ css: rewritten.css }).map((statement) => {
3309
3903
  statement.path.startsWith(".") || fail2(
@@ -3317,26 +3911,27 @@ async function inlineLocalCssImports({
3317
3911
  "Theme CSS imports cannot escape their publish.os scope.",
3318
3912
  { importPath: statement.path, source: filePath }
3319
3913
  ), { ...statement, path };
3320
- }), importedCss = await Promise.all(
3321
- imports.map(async (statement) => {
3322
- let body = await inlineLocalCssImports({
3323
- filePath: statement.path,
3324
- rootDir,
3325
- publishOsDir,
3326
- assets,
3327
- seen: new Set(seen)
3328
- });
3329
- return statement.media && (body = `@media ${statement.media} {
3914
+ }), importedCss = [];
3915
+ for (let statement of imports) {
3916
+ let body = await inlineLocalCssImports({
3917
+ filePath: statement.path,
3918
+ rootDir,
3919
+ publishOsDir,
3920
+ assets,
3921
+ resourceAdmission,
3922
+ onWarning,
3923
+ seen: new Set(seen)
3924
+ });
3925
+ statement.media && (body = `@media ${statement.media} {
3330
3926
  ${body}
3331
3927
  }`), statement.supports && (body = `@supports ${/^[^():]+:/.test(
3332
- statement.supports.replace(/\/\*[\s\S]*?\*\//g, " ")
3333
- ) ? `(${statement.supports})` : statement.supports} {
3928
+ statement.supports.replace(/\/\*[\s\S]*?\*\//g, " ")
3929
+ ) ? `(${statement.supports})` : statement.supports} {
3334
3930
  ${body}
3335
3931
  }`), statement.layer !== void 0 && (body = `@layer${statement.layer === !0 ? "" : ` ${statement.layer}`} {
3336
3932
  ${body}
3337
- }`), body;
3338
- })
3339
- );
3933
+ }`), importedCss.push(body);
3934
+ }
3340
3935
  return imports.reduceRight(
3341
3936
  (body, statement, index) => `${body.slice(0, statement.start)}${importedCss[index] ?? ""}${body.slice(statement.end)}`,
3342
3937
  rewritten.css
@@ -3423,7 +4018,10 @@ function ogInput({
3423
4018
  }
3424
4019
  async function createThemeBuildPlan({
3425
4020
  config,
3426
- publishOsDir
4021
+ publishOsDir,
4022
+ media,
4023
+ resourceAdmission: suppliedResourceAdmission,
4024
+ onWarning
3427
4025
  }) {
3428
4026
  let sources = /* @__PURE__ */ new Map(), rootSource = await themeSource({
3429
4027
  folderPath: publishOsDir,
@@ -3439,41 +4037,46 @@ async function createThemeBuildPlan({
3439
4037
  source && sources.set(group.key, source);
3440
4038
  })
3441
4039
  );
3442
- let assets = /* @__PURE__ */ new Map(), defaultsTemplate = await readFile5(THEME_DEFAULTS_PATH, "utf-8"), rootCss = rootSource ? await inlineLocalCssImports({
4040
+ let assets = /* @__PURE__ */ new Map(), resourceAdmission = suppliedResourceAdmission ?? createThemeResourceAdmission({ media, publishOsDir }), warnedFiles = /* @__PURE__ */ new Set(), warn = (warning) => {
4041
+ warning.file && warnedFiles.has(warning.file) || (warning.file && warnedFiles.add(warning.file), onWarning?.(warning));
4042
+ }, defaultsTemplate = await readFile5(THEME_DEFAULTS_PATH, "utf-8"), rootCss = rootSource ? await inlineLocalCssImports({
3443
4043
  filePath: rootSource.filePath,
3444
4044
  rootDir: publishOsDir,
3445
4045
  publishOsDir,
3446
- assets
4046
+ assets,
4047
+ resourceAdmission,
4048
+ onWarning: warn
3447
4049
  }) : "", groups = {};
3448
- return await Promise.all(
3449
- Object.values(config.groups).map(async (group) => {
3450
- let source = sources.get(group.key), selectors = groupSelectors(config, group);
3451
- if (!source || group.key === ROOT_CONTENT_GROUP_KEY) {
3452
- groups[group.key] = {
3453
- css: "",
3454
- hasCss: !1,
3455
- selector: selectors.join(`,
3456
- `)
3457
- };
3458
- return;
3459
- }
3460
- let authoredCss = await inlineLocalCssImports({
3461
- filePath: source.filePath,
3462
- rootDir: group.folderPath,
3463
- publishOsDir,
3464
- assets
3465
- });
4050
+ for (let group of Object.values(config.groups)) {
4051
+ let source = sources.get(group.key), selectors = groupSelectors(config, group);
4052
+ if (!source || group.key === ROOT_CONTENT_GROUP_KEY) {
3466
4053
  groups[group.key] = {
3467
- css: `/* ${group.key} */
4054
+ css: "",
4055
+ hasCss: !1,
4056
+ selector: selectors.join(`,
4057
+ `)
4058
+ };
4059
+ continue;
4060
+ }
4061
+ let authoredCss = await inlineLocalCssImports({
4062
+ filePath: source.filePath,
4063
+ rootDir: group.folderPath,
4064
+ publishOsDir,
4065
+ assets,
4066
+ resourceAdmission,
4067
+ onWarning: warn
4068
+ });
4069
+ groups[group.key] = {
4070
+ css: `/* ${group.key} */
3468
4071
  ${derivedThemeCss(defaultsTemplate, selectors.join(`,
3469
4072
  `))}${scopeGroupThemeCss({ css: authoredCss, scopeSelectors: selectors, sourcePath: source.relativePath })}
3470
4073
  `,
3471
- hasCss: !0,
3472
- selector: selectors.join(`,
4074
+ hasCss: !0,
4075
+ selector: selectors.join(`,
3473
4076
  `)
3474
- };
3475
- })
3476
- ), {
4077
+ };
4078
+ }
4079
+ return {
3477
4080
  assets: [...assets.values()].sort(
3478
4081
  (left, right) => left.sourcePath.localeCompare(right.sourcePath)
3479
4082
  ),
@@ -3486,6 +4089,206 @@ ${derivedThemeCss(defaultsTemplate, selectors.join(`,
3486
4089
  };
3487
4090
  }
3488
4091
 
4092
+ // src/lib/runtime/metadata/discovery-content.ts
4093
+ function discoveryOrigin(manifest, group) {
4094
+ return (group ? manifest.groups[group]?.origin : void 0) ?? manifest.site.origin;
4095
+ }
4096
+ function pathWithin(path, basePath) {
4097
+ return basePath === "/" || path === basePath || path.startsWith(`${basePath}/`);
4098
+ }
4099
+ function discoveryPages({
4100
+ manifest,
4101
+ group,
4102
+ 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
4111
+ }) {
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
+ );
4126
+ }
4127
+
4128
+ // src/lib/runtime/metadata/feed-serialization.ts
4129
+ function xmlEscape(value) {
4130
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
4131
+ }
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
+ function serializeRssFeed(input) {
4145
+ let description = input.description ?? input.title, items = input.pages.map((page) => {
4146
+ let publishedAt = page.publishedAt, pubDate = publishedAt ? `
4147
+ <pubDate>${xmlEscape(new Date(publishedAt).toUTCString())}</pubDate>` : "", itemDescription = page.description ? `
4148
+ <description>${xmlEscape(page.description)}</description>` : "";
4149
+ return ` <item>
4150
+ <title>${xmlEscape(page.title)}</title>
4151
+ <link>${xmlEscape(page.url)}</link>
4152
+ <guid>${xmlEscape(page.url)}</guid>${pubDate}${itemDescription}
4153
+ </item>`;
4154
+ }).join(`
4155
+ `);
4156
+ return `<?xml version="1.0" encoding="UTF-8"?>
4157
+ <rss version="2.0">
4158
+ <channel>
4159
+ <title>${xmlEscape(input.title)}</title>
4160
+ <link>${xmlEscape(input.homeUrl)}</link>
4161
+ <description>${xmlEscape(description)}</description>
4162
+ ${items}
4163
+ </channel>
4164
+ </rss>
4165
+ `;
4166
+ }
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
+ function serializeJsonFeed(input) {
4189
+ return `${JSON.stringify(
4190
+ {
4191
+ version: "https://jsonfeed.org/version/1.1",
4192
+ title: input.title,
4193
+ description: input.description,
4194
+ home_page_url: input.homeUrl,
4195
+ items: input.pages.map((page) => ({
4196
+ id: page.url,
4197
+ url: page.url,
4198
+ title: page.title,
4199
+ summary: page.description,
4200
+ content_text: page.body,
4201
+ date_published: page.publishedAt,
4202
+ date_modified: page.updatedAt,
4203
+ tags: page.tags
4204
+ }))
4205
+ },
4206
+ null,
4207
+ 2
4208
+ )}
4209
+ `;
4210
+ }
4211
+
4212
+ // src/lib/runtime/metadata/llms-serialization.ts
4213
+ function inlineText(value) {
4214
+ return value.replace(/[\r\n]+/g, " ").replace(/([\\`*_[\]<>])/g, "\\$1");
4215
+ }
4216
+ function markdownLink2(link) {
4217
+ let url = link.url.replace(
4218
+ /[\s()<>]/g,
4219
+ (value) => value === "(" ? "%28" : value === ")" ? "%29" : encodeURIComponent(value)
4220
+ );
4221
+ return `[${inlineText(link.title)}](${url})`;
4222
+ }
4223
+ 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
+ ];
4234
+ return `## ${inlineText(section.title)}
4235
+
4236
+ ${lines.join(`
4237
+ `)}`;
4238
+ }).join(`
4239
+
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(
4248
+ (link) => `- ${markdownLink2(link)}`
4249
+ ), otherGroupSection = otherGroupLines.length ? `
4250
+
4251
+ ## Other content groups
4252
+
4253
+ ${otherGroupLines.join(`
4254
+ `)}` : "";
4255
+ return `# ${inlineText(input.title)}
4256
+
4257
+ > ${inlineText(input.summary)}
4258
+
4259
+ ${body}${linkSection}${otherGroupSection}
4260
+ `;
4261
+ }
4262
+ function serializeLlmsFull(input) {
4263
+ let encoder = new TextEncoder(), byteLength = (value) => encoder.encode(value).byteLength, references = [
4264
+ input.indexUrl ? `> LLMS index: ${input.indexUrl}` : void 0,
4265
+ input.sitemapUrl ? `> Sitemap: ${input.sitemapUrl}` : void 0
4266
+ ].filter(Boolean).join(`
4267
+ `), header = `# ${input.title} Full Content
4268
+
4269
+ ${references ? `${references}
4270
+
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
+ )) {
4275
+ let section = `---
4276
+
4277
+ ${artifact.body.trim()}
4278
+
4279
+ `, sectionBytes = byteLength(section);
4280
+ if (totalBytes + sectionBytes > input.maxBytes) {
4281
+ truncated = !0;
4282
+ break;
4283
+ }
4284
+ sections.push(section), totalBytes += sectionBytes, included += 1;
4285
+ }
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}`;
4290
+ }
4291
+
3489
4292
  // src/lib/compiler/nextjs/compose.ts
3490
4293
  var BANNER = `// AUTO-GENERATED by the PublishOS framework - do not edit.
3491
4294
  `, MARKDOWN_ARTIFACT_SHARD_MAX_BYTES = 256e3, MARKDOWN_ARTIFACT_SHARD_MAX_PAGES = 250, DEFAULT_LLMS_FULL_MAX_BYTES = 1e6;
@@ -3510,31 +4313,29 @@ function assertComposeOutputDirSafe(options) {
3510
4313
  function childRouteDir(routeDir, child) {
3511
4314
  return routeDir ? `${routeDir}/${child}` : child;
3512
4315
  }
3513
- var installedFramework2 = readInstalledFrameworkLocation(), KERNEL_DIR = resolve7(installedFramework2.packageRoot, "compiler-assets"), NEXTJS_KERNEL_DIR = resolve7(KERNEL_DIR, "nextjs"), DEFAULTS_DIR = resolve7(NEXTJS_KERNEL_DIR, "defaults"), SHIMS_DIR = resolve7(NEXTJS_KERNEL_DIR, "shims"), RUNTIME_DIST_DIR = resolve7(installedFramework2.packageRoot, "dist"), DIRECT_SHIMS_DIR = resolve7(NEXTJS_KERNEL_DIR, "direct-assets", "shims"), DIRECT_DESIGN_SYSTEM_SHIMS = [
3514
- "design-system-page.tsx",
3515
- "design-system-progress-page.tsx",
3516
- "design-system-links-page.tsx"
3517
- ];
3518
- async function hasDirectDesignSystemRoute() {
3519
- let files = await Promise.all(
3520
- DIRECT_DESIGN_SYSTEM_SHIMS.map(async (file) => ({
3521
- file,
3522
- present: await fileExists5(resolve7(DIRECT_SHIMS_DIR, file))
3523
- }))
3524
- );
3525
- if (!files.some(({ present }) => present)) return !1;
3526
- let missing = files.filter(({ present }) => !present).map(({ file }) => file);
3527
- if (missing.length > 0)
3528
- throw new AppError({
3529
- code: "FRAMEWORK_RELEASE_INVALID",
3530
- message: "The local design-system route assets are incomplete.",
3531
- details: { missing }
3532
- });
3533
- return !0;
3534
- }
4316
+ var installedFramework2 = readInstalledFrameworkLocation(), KERNEL_DIR = resolve7(installedFramework2.packageRoot, "compiler-assets"), NEXTJS_KERNEL_DIR = resolve7(KERNEL_DIR, "nextjs"), DEFAULTS_DIR = resolve7(NEXTJS_KERNEL_DIR, "defaults"), SHIMS_DIR = resolve7(NEXTJS_KERNEL_DIR, "shims"), RUNTIME_DIST_DIR = resolve7(installedFramework2.packageRoot, "dist");
3535
4317
  async function fileExists5(path) {
3536
4318
  return await stat6(path).then((entry) => entry.isFile()).catch(() => !1);
3537
4319
  }
4320
+ async function componentResolveAliases({
4321
+ outputDir,
4322
+ sourceDir
4323
+ }) {
4324
+ let extensions = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"], aliases = {};
4325
+ for (let alias of supportedRuntimeComponentAliases()) {
4326
+ let componentPath = alias.slice(12), base = resolve7(sourceDir, "components", componentPath), candidates = [
4327
+ ...extensions.map((extension) => `${base}${extension}`),
4328
+ ...extensions.map((extension) => resolve7(base, `index${extension}`))
4329
+ ], authored;
4330
+ for (let candidate of candidates)
4331
+ if (await fileExists5(candidate)) {
4332
+ authored = candidate;
4333
+ break;
4334
+ }
4335
+ aliases[alias] = authored ? `./${relative4(outputDir, authored).replaceAll("\\", "/")}` : `@publish.os/nextjs/kernel/components/${componentPath}`;
4336
+ }
4337
+ return aliases;
4338
+ }
3538
4339
  async function readKernelFile(path) {
3539
4340
  return await readFile6(resolve7(KERNEL_DIR, path), "utf-8");
3540
4341
  }
@@ -3572,6 +4373,13 @@ async function walkPublishOsSourceFiles(dir) {
3572
4373
  skipDirectory: isExcludedSourcePath
3573
4374
  })).filter((path) => !isExcludedSourcePath(path));
3574
4375
  }
4376
+ async function mapLocalAssetUrlsInCss(outputDir) {
4377
+ for (let path of await walkFiles(outputDir)) {
4378
+ if (!path.endsWith(".css")) continue;
4379
+ let absolutePath = resolve7(outputDir, path), css = await readFile6(absolutePath, "utf-8"), mapped = mapSourceAssetCss(css, absolutePath);
4380
+ mapped !== css && await writeText(absolutePath, mapped);
4381
+ }
4382
+ }
3575
4383
  function createIdeSourceArtifacts(options) {
3576
4384
  return {
3577
4385
  mediaModule: mediaManifestModule(options.mediaManifest)
@@ -3658,117 +4466,30 @@ function defaultSourceGlobalsEntrypoint() {
3658
4466
  '@import "./css/layout.css";',
3659
4467
  '@import "./css/zindex.css";',
3660
4468
  '@import "./css/typography.css";',
3661
- '@import "./css/animation.css";',
3662
- '@import "./css/media.css";',
3663
- '@import "./theme-base.generated.css";',
3664
- '@import "./theme-defaults.generated.css";',
3665
- '@import "./components.generated.css";',
3666
- '@import "./theme.css";',
3667
- '@import "./publishos.css";'
3668
- ].join(`
3669
- `)}
3670
- `;
3671
- }
3672
- function componentDefaultsCss(manifest) {
3673
- return `${manifest.families.map((family) => `@import "./css/components/${family}.css";`).join(`
3674
- `)}
3675
- `;
3676
- }
3677
- async function appGlobalsCss(manifest) {
3678
- return (await readKernelFile("nextjs/defaults/app/globals.css")).replace(
3679
- "/* PUBLISHOS_TAILWIND_SOURCES */",
3680
- manifest.sources.map((source) => `@source "${source}";`).join(`
3681
- `)
3682
- );
3683
- }
3684
- async function designSystemPreviewModule(publishOsDir) {
3685
- let hasButton = await fileExists5(
3686
- resolve7(publishOsDir, "components", "ui", "button.tsx")
3687
- ), hasMdxLink = await fileExists5(
3688
- resolve7(publishOsDir, "components", "mdx", "link.tsx")
3689
- ), hasNavbar = await fileExists5(
3690
- resolve7(publishOsDir, "components", "blocks", "navbar.tsx")
3691
- ), imports = [
3692
- 'import { DesignSystemPreview } from "@components/blocks/design-system-preview";',
3693
- 'import type { DesignSystemPreviewFixture } from "@components/blocks/design-system-preview";',
3694
- 'import { Tabs, TabsList, TabsPanel, TabsTab } from "@components/ui/tabs";'
3695
- ];
3696
- hasButton && imports.push('import { Button } from "@components/ui/button";'), hasMdxLink && imports.push('import { Link as MdxLink } from "@components/mdx/link";'), hasNavbar && (imports.push('import { Navbar } from "@components/blocks/navbar";'), imports.push(
3697
- 'import type { SiteData, TreeNode } from "@publish.os/nextjs/types";'
3698
- ));
3699
- let declarations = [], uiFixtures = hasButton ? `[
3700
- {
3701
- name: "Button",
3702
- children: (
3703
- <div className="flex flex-col items-center gap-3">
3704
- <Button>Solid</Button>
3705
- <Button variant="soft">Soft</Button>
3706
- <Button disabled>Disabled</Button>
3707
- </div>
3708
- ),
3709
- },
3710
- ]` : "[]", mdxFixtures = hasMdxLink ? `[
3711
- {
3712
- name: "Link",
3713
- children: (
3714
- <div className="flex flex-col items-center gap-2 text-sm">
3715
- <MdxLink href="/ds">Internal link</MdxLink>
3716
- <MdxLink href="#">Example link</MdxLink>
3717
- </div>
3718
- ),
3719
- },
3720
- ]` : "[]";
3721
- hasNavbar && declarations.push(`const previewSite: SiteData = {
3722
- name: "publishOS Preview",
3723
- };
3724
-
3725
- const previewTree: TreeNode[] = [
3726
- {
3727
- type: "section",
3728
- id: "docs",
3729
- title: "Docs",
3730
- path: "/docs",
3731
- children: [],
3732
- },
3733
- {
3734
- type: "section",
3735
- id: "blog",
3736
- title: "Blog",
3737
- path: "/blog",
3738
- children: [],
3739
- },
3740
- ];`);
3741
- let blockFixtures = hasNavbar ? `[
3742
- {
3743
- name: "Navbar",
3744
- children: (
3745
- <Navbar site={previewSite} tree={previewTree} />
3746
- ),
3747
- },
3748
- ]` : "[]";
3749
- return `${imports.join(`
3750
- `)}
3751
-
3752
- ${declarations.join(`
3753
-
4469
+ '@import "./css/animation.css";',
4470
+ '@import "./css/media.css";',
4471
+ '@import "./theme-base.generated.css";',
4472
+ '@import "./theme-defaults.generated.css";',
4473
+ '@import "./components.generated.css";',
4474
+ '@import "./theme.css";',
4475
+ '@import "./publishos.css";',
4476
+ '@import "./css/components/code-block.css";'
4477
+ ].join(`
3754
4478
  `)}
3755
-
3756
- const uiFixtures: DesignSystemPreviewFixture[] = ${uiFixtures};
3757
- const mdxFixtures: DesignSystemPreviewFixture[] = ${mdxFixtures};
3758
- const blockFixtures: DesignSystemPreviewFixture[] = ${blockFixtures};
3759
-
3760
- export default function GeneratedDesignSystemPreview() {
3761
- return (
3762
- <DesignSystemPreview
3763
- blocks={blockFixtures}
3764
- mdx={mdxFixtures}
3765
- tabComponents={{ Tabs, TabsList, TabsPanel, TabsTab }}
3766
- ui={uiFixtures}
3767
- />
3768
- );
4479
+ `;
3769
4480
  }
4481
+ function componentDefaultsCss(manifest) {
4482
+ return `${manifest.families.map((family) => `@import "./css/components/${family}.css";`).join(`
4483
+ `)}
3770
4484
  `;
3771
4485
  }
4486
+ async function appGlobalsCss(manifest) {
4487
+ return (await readKernelFile("nextjs/defaults/app/globals.css")).replace(
4488
+ "/* PUBLISHOS_TAILWIND_SOURCES */",
4489
+ manifest.sources.map((source) => `@source "${source}";`).join(`
4490
+ `)
4491
+ );
4492
+ }
3772
4493
  async function mdxSourceComponentsModule(publishOsDir) {
3773
4494
  let mdxDir = resolve7(publishOsDir, "components", "mdx"), files = (await readdir3(mdxDir).catch(() => [])).filter((file) => file.endsWith(".tsx")).sort(), imports = [
3774
4495
  'import type { MDXComponents } from "@publish.os/nextjs/mdx/mdx";'
@@ -4204,12 +4925,21 @@ function ogFontRoleInput(font) {
4204
4925
  subsets: font.subsets,
4205
4926
  weight: font.weight,
4206
4927
  ...font.localFiles ? {
4207
- files: font.localFiles.map((file) => ({
4208
- configuredPath: file.configuredPath,
4209
- path: file.sourcePath,
4210
- style: (Array.isArray(file.style), file.style),
4211
- weight: file.declaredWeight
4212
- }))
4928
+ files: font.localFiles.map((file) => {
4929
+ if (!file.admittedContents)
4930
+ throw new AppError({
4931
+ code: "FRAMEWORK_FONT_ASSET_UNAVAILABLE",
4932
+ message: `The local font file was not admitted: ${file.configuredPath}`,
4933
+ details: { path: file.configuredPath }
4934
+ });
4935
+ return {
4936
+ configuredPath: file.configuredPath,
4937
+ data: file.admittedContents,
4938
+ path: file.sourcePath,
4939
+ style: file.style,
4940
+ weight: file.declaredWeight
4941
+ };
4942
+ })
4213
4943
  } : {}
4214
4944
  };
4215
4945
  }
@@ -4263,7 +4993,8 @@ async function validateLocalVariableFonts({
4263
4993
  isVariableFontWeight(weight) && (file.weight = await validateVariableFontFile({
4264
4994
  configuredPath: file.configuredPath,
4265
4995
  sourcePath: file.sourcePath,
4266
- weight
4996
+ weight,
4997
+ contents: file.admittedContents
4267
4998
  }));
4268
4999
  })
4269
5000
  ), localFiles.length === 1 && isVariableFontWeight(font.weight) && (font.weight = localFiles[0]?.weight), localFiles.length === 1 && (font.style = localFiles[0]?.style ?? font.style);
@@ -4274,19 +5005,51 @@ async function fontBuildPlan({
4274
5005
  config,
4275
5006
  publishOsDir,
4276
5007
  prepareAssets,
4277
- themeGroups
5008
+ themeGroups,
5009
+ resourceAdmission
4278
5010
  }) {
4279
5011
  let rootFonts = resolvedRootFonts(config, publishOsDir), fontsByIdentity = /* @__PURE__ */ new Map(), register = (font) => {
4280
5012
  fontsByIdentity.has(font.identity) || fontsByIdentity.set(font.identity, font);
4281
5013
  };
4282
5014
  for (let font of Object.values(rootFonts)) register(font);
4283
- let groups = {};
5015
+ let groupOverridesByKey = /* @__PURE__ */ new Map(), groups = {};
4284
5016
  for (let group of Object.values(config.groups)) {
4285
5017
  if (!hasFontOverrides(group)) continue;
4286
5018
  let overrides = groupFontOverrides(group, publishOsDir);
5019
+ groupOverridesByKey.set(group.key, overrides);
4287
5020
  for (let font of Object.values(overrides))
4288
5021
  font && register(font);
4289
5022
  }
5023
+ let admittedFonts = /* @__PURE__ */ new Map(), allFontInstances = [
5024
+ ...Object.values(rootFonts),
5025
+ ...[...groupOverridesByKey.values()].flatMap(
5026
+ (overrides) => Object.values(overrides).filter(
5027
+ (font) => !!font
5028
+ )
5029
+ )
5030
+ ];
5031
+ for (let font of allFontInstances)
5032
+ for (let file of font.localFiles ?? []) {
5033
+ let sourcePath = resolve7(
5034
+ publishOsDir,
5035
+ localFontSourcePath(file.configuredPath)
5036
+ ), admitted = admittedFonts.get(sourcePath);
5037
+ if (!admitted) {
5038
+ let result = await resourceAdmission.admit({
5039
+ filePath: file.configuredPath,
5040
+ relativePath: relative4(publishOsDir, sourcePath),
5041
+ sourcePath
5042
+ });
5043
+ if (!result.asset)
5044
+ throw new AppError({
5045
+ code: "FRAMEWORK_FONT_ASSET_UNAVAILABLE",
5046
+ message: `The local font file could not be admitted: ${file.configuredPath}`,
5047
+ details: { path: file.configuredPath }
5048
+ });
5049
+ admitted = result.asset.contents, admittedFonts.set(sourcePath, admitted);
5050
+ }
5051
+ file.admittedContents = admitted;
5052
+ }
4290
5053
  await validateLocalVariableFonts({
4291
5054
  fonts: fontsByIdentity.values(),
4292
5055
  publishOsDir
@@ -4321,7 +5084,7 @@ async function fontBuildPlan({
4321
5084
  message: `Theme scope is missing for group "${group.key}"`,
4322
5085
  details: { group: group.key }
4323
5086
  });
4324
- let overrides = groupFontOverrides(group, publishOsDir), overrideFonts = Object.values(overrides).filter(
5087
+ let overrides = groupOverridesByKey.get(group.key) ?? {}, overrideFonts = Object.values(overrides).filter(
4325
5088
  Boolean
4326
5089
  ), imports = Array.from(new Set(overrideFonts.map(importFor))), classNameExport = `${camelCase(group.key)}FontClassName`, module = `${BANNER}${imports.join(`
4327
5090
  `)}
@@ -4367,9 +5130,8 @@ ${declaredFontRoles(group.fonts).map(
4367
5130
  Object.values(config.groups).filter((group) => hasConfiguredOgRole(group)).map((group) => {
4368
5131
  let resolved = { main: rootFonts.main, mono: rootFonts.mono };
4369
5132
  for (let key of group.ancestry) {
4370
- let ancestor = config.groups[key];
4371
- if (!ancestor) continue;
4372
- let overrides = groupFontOverrides(ancestor, publishOsDir);
5133
+ if (!config.groups[key]) continue;
5134
+ let overrides = groupOverridesByKey.get(key) ?? {};
4373
5135
  overrides.main && (resolved.main = overrides.main), overrides.mono && (resolved.mono = overrides.mono);
4374
5136
  }
4375
5137
  return [
@@ -4426,7 +5188,7 @@ ${declaredFontRoles(group.fonts).map(
4426
5188
  assets: [...fontsByIdentity.values()].flatMap(
4427
5189
  (font) => (font.localFiles ?? []).map((file) => ({
4428
5190
  dest: file.generatedPath,
4429
- src: file.sourcePath
5191
+ data: file.admittedContents
4430
5192
  }))
4431
5193
  ),
4432
5194
  root: {
@@ -4471,7 +5233,7 @@ ${Object.entries(rootFonts).sort(([left], [right]) => left.localeCompare(right))
4471
5233
  ...ogFontManifest ? { og: ogFontManifest } : {}
4472
5234
  };
4473
5235
  }
4474
- async function overlaySource(publishOsDir, sourceDir, config, fonts, theme) {
5236
+ async function overlaySource(publishOsDir, sourceDir, config, fonts, theme, pages) {
4475
5237
  let defaultsUsed = [], sourceOverrides = [], sourceConfigPath = resolve7(publishOsDir, "publish.os.config.json");
4476
5238
  await fileExists5(sourceConfigPath) && (await copyText(
4477
5239
  sourceConfigPath,
@@ -4502,6 +5264,9 @@ async function overlaySource(publishOsDir, sourceDir, config, fonts, theme) {
4502
5264
  resolve7(sourceDir, "_internal", "mdx-source-components.generated.ts"),
4503
5265
  await mdxSourceComponentsModule(publishOsDir)
4504
5266
  ), defaultsUsed.push("_internal/mdx-source-components.generated.ts"), await writeText(
5267
+ resolve7(sourceDir, "_internal", "mdx-icon-components.generated.ts"),
5268
+ mdxIconComponentsModule(pages)
5269
+ ), defaultsUsed.push("_internal/mdx-icon-components.generated.ts"), await writeText(
4505
5270
  resolve7(sourceDir, "_internal", "mdx-components.generated.tsx"),
4506
5271
  BANNER + await renderShim("site-mdx-components.tsx", {})
4507
5272
  ), defaultsUsed.push("_internal/mdx-components.generated.tsx"), await writeText(
@@ -4525,29 +5290,19 @@ async function overlaySource(publishOsDir, sourceDir, config, fonts, theme) {
4525
5290
  for (let module of fonts.modules)
4526
5291
  await writeText(resolve7(dirname8(sourceDir), module.path), module.content), defaultsUsed.push(module.path.replace(/^source\//, ""));
4527
5292
  for (let asset of fonts.assets) {
4528
- if (await assertSourceDependency({
4529
- publishOsDir,
4530
- path: asset.src,
4531
- sourceFile: "publish.os.config.json"
4532
- }), !await fileExists5(asset.src))
4533
- throw new AppError({
4534
- code: "FRAMEWORK_FONT_CONFIG_INVALID",
4535
- message: `Local font file does not exist: ${asset.src}`,
4536
- details: { path: asset.src }
4537
- });
4538
- await copyTreeIfExists(asset.src, resolve7(dirname8(sourceDir), asset.dest)), defaultsUsed.push(asset.dest.replace(/^source\//, ""));
5293
+ let destination = resolve7(dirname8(sourceDir), asset.dest);
5294
+ await mkdir6(dirname8(destination), { recursive: !0 }), await writeFile6(destination, asset.data), defaultsUsed.push(asset.dest.replace(/^source\//, ""));
4539
5295
  }
4540
5296
  for (let asset of fonts.downloadedAssets) {
4541
5297
  let destination = resolve7(dirname8(sourceDir), asset.dest);
4542
5298
  await mkdir6(dirname8(destination), { recursive: !0 }), await writeFile6(destination, asset.data);
4543
5299
  }
4544
- for (let asset of theme.assets)
4545
- await copyTreeIfExists(
4546
- asset.sourcePath,
4547
- resolve7(dirname8(sourceDir), "public", asset.outputPath)
4548
- ), sourceOverrides.push(
5300
+ for (let asset of theme.assets) {
5301
+ let destination = resolve7(dirname8(sourceDir), "public", asset.outputPath);
5302
+ await mkdir6(dirname8(destination), { recursive: !0 }), await writeFile6(destination, asset.contents), sourceOverrides.push(
4549
5303
  relative4(publishOsDir, asset.sourcePath).split("\\").join("/")
4550
5304
  );
5305
+ }
4551
5306
  for (let file of ["layout.tsx", "index.tsx", "page.tsx"]) {
4552
5307
  let projectPath = resolve7(publishOsDir, file), dest = resolve7(sourceDir, file);
4553
5308
  await fileExists5(projectPath) ? (await copyTreeIfExists(projectPath, dest), sourceOverrides.push(file)) : (await copyTreeIfExists(resolve7(DEFAULTS_DIR, "root", file), dest), defaultsUsed.push(file));
@@ -4604,10 +5359,6 @@ export default function InheritedGroupLayout({ children }: LayoutProps) { return
4604
5359
  resolve7(publishOsDir, "components"),
4605
5360
  resolve7(sourceDir, "components"),
4606
5361
  publishOsDir
4607
- ), await copyTreeIfExists(
4608
- resolve7(publishOsDir, "public"),
4609
- resolve7(dirname8(sourceDir), "public"),
4610
- publishOsDir
4611
5362
  ), { defaultsUsed, sourceOverrides };
4612
5363
  }
4613
5364
  function manifestModule(manifest) {
@@ -4681,6 +5432,20 @@ function outputRoutePath(outputPath) {
4681
5432
  function outputPathWithinGroup(path, groupPath) {
4682
5433
  return groupPath === "/" || path === groupPath || path.startsWith(`${groupPath}/`);
4683
5434
  }
5435
+ function outputDirectory2(path) {
5436
+ return path.slice(0, path.lastIndexOf("/") + 1) || "/";
5437
+ }
5438
+ function discoveryPlanCovers({
5439
+ manifest,
5440
+ plan,
5441
+ group,
5442
+ path
5443
+ }) {
5444
+ if (effectiveOrigin(manifest, plan.group) !== effectiveOrigin(manifest, group))
5445
+ return !1;
5446
+ let scopeGroup = plan.scopeGroup ?? plan.group;
5447
+ return scopeGroup ? outputPathWithinGroup(path, manifest.groups[scopeGroup]?.path ?? "/") ? plan.scope === "site" ? !0 : group === scopeGroup || plan.scope !== "self" && group.startsWith(`${scopeGroup}/`) : !1 : !0;
5448
+ }
4684
5449
  function outputFactory(kind) {
4685
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" };
4686
5451
  }
@@ -4701,13 +5466,13 @@ export const GET = createPrecomputedFeedRoute(agentTextOutputs[${JSON.stringify(
4701
5466
  export const HEAD = GET;
4702
5467
  `;
4703
5468
  let options = {};
4704
- plan.scope && (options.scope = plan.scope), plan.group && (options.group = plan.group), plan.limit && (options.limit = plan.limit), plan.status && (options.status = plan.status), plan.includeDescriptions && (options.includeDescriptions = plan.includeDescriptions), plan.links?.length && (options.links = plan.links);
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);
4705
5470
  let args = Object.keys(options).length > 0 ? `manifest, ${JSON.stringify(options, null, 2)}` : "manifest";
4706
5471
  return plan.kind === "sitemap-md" ? `import manifest from "@manifest";
4707
5472
  import { markdownArtifactManifest } from "@source/.publishos/markdown-index.generated";
4708
5473
  import { ${factory} } from "@publish.os/nextjs/kernel/routes/metadata/${routeModule}";
4709
5474
 
4710
- export const GET = ${factory}(manifest, markdownArtifactManifest);
5475
+ export const GET = ${factory}(manifest, markdownArtifactManifest, ${JSON.stringify(options, null, 2)});
4711
5476
  ` : `import manifest from "@manifest";
4712
5477
  import { ${factory} } from "@publish.os/nextjs/kernel/routes/metadata/${routeModule}";
4713
5478
 
@@ -4740,36 +5505,51 @@ function outputHome(manifest, plan) {
4740
5505
  function llmsSummary(manifest, title) {
4741
5506
  return manifest.site.description ? `> ${manifest.site.description}` : `> Agent-readable index for ${title}.`;
4742
5507
  }
4743
- function llmsTextBody(manifest, plan) {
4744
- let home = outputHome(manifest, plan), pages = manifest.pages.filter((page) => page.status === "published").filter((page) => pageInOutputScope(page, plan, manifest)), lines = (plan.limit ? pages.slice(0, plan.limit) : pages).map((page) => {
4745
- let description = plan.includeDescriptions && page.description ? ` - ${page.description}` : "";
4746
- return `- [${page.title}](${canonicalUrl(manifest, page.path, page.group)})${description}`;
4747
- }), linkLines = (plan.links ?? []).map(
4748
- (link) => `- [${link.title}](${canonicalUrl(manifest, link.path, link.group)})`
4749
- ), linkSection = linkLines.length ? `
4750
-
4751
- ## Group LLM indexes
4752
-
4753
- ${linkLines.join(`
4754
- `)}` : "";
4755
- return `# ${home.title}
4756
-
4757
- ${llmsSummary(manifest, home.title)}
4758
-
4759
- ## Pages
4760
-
4761
- ${lines.join(`
4762
- `)}${linkSection}
4763
- `;
5508
+ function llmsTextBody(manifest, plan, markdownPaths) {
5509
+ let home = outputHome(manifest, plan), pages = discoveryPages({
5510
+ 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`,
5538
+ url: canonicalUrl(manifest, link.path, link.group)
5539
+ }))
5540
+ })),
5541
+ includeDescriptions: plan.includeDescriptions,
5542
+ otherGroupLinks: plan.otherGroupLinks?.map((link) => ({
5543
+ title: link.title,
5544
+ url: canonicalUrl(manifest, link.path, link.group)
5545
+ }))
5546
+ });
4764
5547
  }
4765
5548
  function feedContentType(kind) {
4766
5549
  if (kind === "rss") return "application/rss+xml; charset=utf-8";
4767
5550
  if (kind === "atom") return "application/atom+xml; charset=utf-8";
4768
5551
  if (kind === "json-feed") return "application/feed+json; charset=utf-8";
4769
5552
  }
4770
- function xmlEscape(value) {
4771
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
4772
- }
4773
5553
  function pageTimestamp(page) {
4774
5554
  let value = page.publishedAt ?? page.updatedAt;
4775
5555
  if (!value) return -1 / 0;
@@ -4783,126 +5563,72 @@ function feedPages(manifest, plan) {
4783
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));
4784
5564
  return plan.limit ? pages.slice(0, plan.limit) : pages;
4785
5565
  }
4786
- function pageDate(page) {
4787
- return page.publishedAt ?? page.updatedAt;
4788
- }
4789
- function latestFeedDate(pages) {
4790
- let timestamp = Math.max(0, ...pages.map((page) => pageTimestamp(page)));
4791
- return new Date(timestamp).toISOString();
4792
- }
4793
- function rssFeedBody(manifest, plan) {
4794
- let home = outputHome(manifest, plan), title = home.title, description = manifest.site.description ?? title, pages = feedPages(manifest, plan), homeUrl = canonicalUrl(manifest, home.path, home.group), items = pages.map((page) => {
4795
- let pageUrl = canonicalUrl(manifest, page.path, page.group), pubDate = page.publishedAt ? `
4796
- <pubDate>${xmlEscape(new Date(page.publishedAt).toUTCString())}</pubDate>` : "", description2 = page.description ? `
4797
- <description>${xmlEscape(page.description)}</description>` : "";
4798
- return ` <item>
4799
- <title>${xmlEscape(page.title)}</title>
4800
- <link>${xmlEscape(pageUrl)}</link>
4801
- <guid>${xmlEscape(pageUrl)}</guid>${pubDate}${description2}
4802
- </item>`;
4803
- }).join(`
4804
- `);
4805
- return `<?xml version="1.0" encoding="UTF-8"?>
4806
- <rss version="2.0">
4807
- <channel>
4808
- <title>${xmlEscape(title)}</title>
4809
- <link>${xmlEscape(homeUrl)}</link>
4810
- <description>${xmlEscape(description)}</description>
4811
- ${items}
4812
- </channel>
4813
- </rss>
4814
- `;
4815
- }
4816
- function atomFeedBody(manifest, plan) {
4817
- let home = outputHome(manifest, plan), title = home.title, pages = feedPages(manifest, plan), homeUrl = canonicalUrl(manifest, home.path, home.group), entries = pages.map((page) => {
4818
- let date = pageDate(page), updated = date ? new Date(date).toISOString() : latestFeedDate([]), pageUrl = canonicalUrl(manifest, page.path, page.group), summary = page.description ? `
4819
- <summary>${xmlEscape(page.description)}</summary>` : "";
4820
- return ` <entry>
4821
- <title>${xmlEscape(page.title)}</title>
4822
- <id>${xmlEscape(pageUrl)}</id>
4823
- <link href="${xmlEscape(pageUrl)}" />
4824
- <updated>${xmlEscape(updated)}</updated>${summary}
4825
- </entry>`;
4826
- }).join(`
4827
- `);
4828
- return `<?xml version="1.0" encoding="UTF-8"?>
4829
- <feed xmlns="http://www.w3.org/2005/Atom">
4830
- <title>${xmlEscape(title)}</title>
4831
- <id>${xmlEscape(homeUrl)}</id>
4832
- <updated>${xmlEscape(latestFeedDate(pages))}</updated>
4833
- ${entries}
4834
- </feed>
4835
- `;
4836
- }
4837
- function jsonFeedBody(manifest, plan) {
5566
+ function generatedFeedInput(manifest, plan) {
4838
5567
  let home = outputHome(manifest, plan), pages = feedPages(manifest, plan);
4839
- return `${JSON.stringify(
4840
- {
4841
- version: "https://jsonfeed.org/version/1.1",
4842
- title: home.title,
4843
- description: manifest.site.description,
4844
- home_page_url: canonicalUrl(manifest, home.path, home.group),
4845
- items: pages.map((page) => ({
4846
- id: canonicalUrl(manifest, page.path, page.group),
4847
- url: canonicalUrl(manifest, page.path, page.group),
4848
- title: page.title,
4849
- summary: page.description,
4850
- content_text: page.body,
4851
- date_published: page.publishedAt,
4852
- date_modified: page.updatedAt,
4853
- tags: page.tags
4854
- }))
4855
- },
4856
- null,
4857
- 2
4858
- )}
4859
- `;
5568
+ return {
5569
+ title: home.title,
5570
+ description: manifest.site.description,
5571
+ homeUrl: canonicalUrl(manifest, home.path, home.group),
5572
+ pages: pages.map((page) => ({
5573
+ url: canonicalUrl(manifest, page.path, page.group),
5574
+ title: page.title,
5575
+ publishedAt: page.publishedAt,
5576
+ updatedAt: page.updatedAt,
5577
+ description: page.description,
5578
+ body: page.body,
5579
+ tags: page.tags
5580
+ }))
5581
+ };
4860
5582
  }
4861
5583
  function feedBody(manifest, plan) {
4862
- if (plan.kind === "rss") return rssFeedBody(manifest, plan);
4863
- if (plan.kind === "atom") return atomFeedBody(manifest, plan);
4864
- if (plan.kind === "json-feed") return jsonFeedBody(manifest, plan);
4865
- }
4866
- function llmsFullTextBody(manifest, artifacts) {
4867
- let home = outputHome(manifest), pageGroups = new Map(
4868
- manifest.pages.map((page) => [page.path, page.group])
4869
- ), visibleArtifacts = artifacts.filter((artifact) => {
4870
- let group = pageGroups.get(artifact.htmlPath);
4871
- return group && effectiveOrigin(manifest, group) === manifest.site.origin;
4872
- }), header = `# ${home.title} Full Content
4873
-
4874
- > LLMS index: ${canonicalUrl(manifest, "/llms.txt")}
4875
- > Sitemap: ${canonicalUrl(manifest, "/sitemap.md")}
4876
-
4877
- `, sections = [], totalBytes = markdownByteSize(header), included = 0, truncated = !1;
4878
- for (let artifact of [...visibleArtifacts].sort(
4879
- (a, b) => a.markdownPath.localeCompare(b.markdownPath)
4880
- )) {
4881
- let section = `---
4882
-
4883
- ${artifact.body.trim()}
4884
-
4885
- `, sectionBytes = markdownByteSize(section);
4886
- if (totalBytes + sectionBytes > DEFAULT_LLMS_FULL_MAX_BYTES) {
4887
- truncated = !0;
4888
- break;
4889
- }
4890
- sections.push(section), totalBytes += sectionBytes, included += 1;
4891
- }
4892
- let footer = truncated ? `
4893
- > Truncated at ${included} of ${visibleArtifacts.length} Markdown pages to stay under ${DEFAULT_LLMS_FULL_MAX_BYTES} bytes.
4894
- ` : "";
4895
- return `${header}${sections.join("")}${footer}`;
5584
+ if (plan.kind === "rss")
5585
+ return serializeRssFeed(generatedFeedInput(manifest, plan));
5586
+ if (plan.kind === "atom")
5587
+ return serializeAtomFeed(generatedFeedInput(manifest, plan));
5588
+ if (plan.kind === "json-feed")
5589
+ return serializeJsonFeed(generatedFeedInput(manifest, plan));
5590
+ }
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
+ );
5605
+ 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,
5609
+ artifacts: visibleArtifacts,
5610
+ maxBytes: DEFAULT_LLMS_FULL_MAX_BYTES
5611
+ });
4896
5612
  }
4897
- function agentTextOutputsModule(manifest, outputPlans, artifacts = []) {
4898
- let outputs = Object.fromEntries(
5613
+ function agentTextOutputsModule(manifest, outputPlans, artifacts = [], availableMarkdownPaths) {
5614
+ let markdownPaths = new Map(
5615
+ artifacts.filter(
5616
+ (artifact) => !availableMarkdownPaths || availableMarkdownPaths.has(artifact.markdownPath)
5617
+ ).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))))
5624
+ ), outputs = Object.fromEntries(
4899
5625
  outputPlans.filter(
4900
5626
  (plan) => plan.kind === "robots" || plan.kind === "llms" || plan.kind === "llms-full" || plan.kind === "rss" || plan.kind === "atom" || plan.kind === "json-feed"
4901
5627
  ).map((plan) => {
4902
5628
  let body = plan.kind === "robots" ? `User-agent: *
4903
5629
  Allow: /
4904
- Sitemap: ${canonicalUrl(manifest, outputPlans.find((output) => output.kind === "sitemap" && !output.group)?.path ?? "/sitemap.xml")}
4905
- ` : plan.kind === "llms-full" ? llmsFullTextBody(manifest, artifacts) : feedBody(manifest, plan) ?? llmsTextBody(manifest, plan);
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);
4906
5632
  return [
4907
5633
  plan.path,
4908
5634
  {
@@ -4917,7 +5643,7 @@ Sitemap: ${canonicalUrl(manifest, outputPlans.find((output) => output.kind === "
4917
5643
  return `export const agentTextOutputs = ${JSON.stringify(outputs, null, 2)} as const;
4918
5644
  `;
4919
5645
  }
4920
- function agentRouteConfig(config, outputPlans, manifest, additionalPublicPaths) {
5646
+ function agentRouteConfig(config, outputPlans, manifest, additionalPublicPaths, notFoundScopes, markdownPaths) {
4921
5647
  return {
4922
5648
  agentDetection: config.agent?.agentDetection ?? !0,
4923
5649
  blockInternalMarkdownRoute: config.agent?.blockInternalMarkdownRoute ?? !0,
@@ -4927,6 +5653,9 @@ function agentRouteConfig(config, outputPlans, manifest, additionalPublicPaths)
4927
5653
  markdownAccept: config.agent?.markdownAccept ?? !0,
4928
5654
  markdownRoutes: config.agent?.markdownRoutes ?? !0,
4929
5655
  plainTextAccept: config.agent?.plainTextAccept ?? !0,
5656
+ notFoundScopes,
5657
+ markdownPaths,
5658
+ outputPaths: outputPlans.map((output) => output.path),
4930
5659
  publicPaths: [
4931
5660
  .../* @__PURE__ */ new Set([
4932
5661
  "/",
@@ -4938,9 +5667,16 @@ function agentRouteConfig(config, outputPlans, manifest, additionalPublicPaths)
4938
5667
  ].sort()
4939
5668
  };
4940
5669
  }
4941
- function agentRouteConfigModule(config, outputPlans, manifest, additionalPublicPaths) {
5670
+ function agentRouteConfigModule(config, outputPlans, manifest, additionalPublicPaths, notFoundScopes, markdownPaths) {
4942
5671
  return `export const agentRouteConfig = ${JSON.stringify(
4943
- agentRouteConfig(config, outputPlans, manifest, additionalPublicPaths),
5672
+ agentRouteConfig(
5673
+ config,
5674
+ outputPlans,
5675
+ manifest,
5676
+ additionalPublicPaths,
5677
+ notFoundScopes,
5678
+ markdownPaths
5679
+ ),
4944
5680
  null,
4945
5681
  2
4946
5682
  )} as const;
@@ -4956,10 +5692,23 @@ function groupBreadcrumbs(manifest, groupKey, title) {
4956
5692
  let group = manifest.groups[groupKey], seenKeys = /* @__PURE__ */ new Set(), groups = [...group?.ancestry ?? [], groupKey].filter((key) => seenKeys.has(key) ? !1 : (seenKeys.add(key), !0)).map((key) => manifest.groups[key]?.title).filter((value) => !!value), seenLabels = /* @__PURE__ */ new Set();
4957
5693
  return [...groups, title].filter((label) => seenLabels.has(label) ? !1 : (seenLabels.add(label), !0)).join(" > ");
4958
5694
  }
4959
- function groupLlmsPath(pageGroup, outputPlans) {
4960
- return outputPlans.find(
4961
- (candidate) => candidate.kind === "llms" && candidate.group === pageGroup
4962
- )?.path;
5695
+ function pageDiscoveryPlan({
5696
+ manifest,
5697
+ outputPlans,
5698
+ page,
5699
+ rootOwnedGroups,
5700
+ kinds
5701
+ }) {
5702
+ return outputPlans.filter(
5703
+ (plan) => kinds.includes(plan.kind) && (plan.group || rootOwnedGroups.has(page.group)) && discoveryPlanCovers({
5704
+ manifest,
5705
+ plan,
5706
+ group: page.group,
5707
+ path: page.path
5708
+ }) && page.path.startsWith(outputDirectory2(plan.path))
5709
+ ).sort(
5710
+ (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
+ )[0];
4963
5712
  }
4964
5713
  function markdownLinkForPage(manifest, page) {
4965
5714
  return `[${page.title}](${canonicalUrl(
@@ -5086,7 +5835,10 @@ function markdownArtifactEnrichment(options) {
5086
5835
  let { next, previous } = adjacentMarkdownPages({
5087
5836
  context: options.context,
5088
5837
  page: options.page
5089
- }), group = options.manifest.groups[options.page.group], sectionLlms = groupLlmsPath(options.page.group, options.outputPlans), related = relatedMarkdownPages({
5838
+ }), group = options.manifest.groups[options.page.group], llms = pageDiscoveryPlan({ ...options, kinds: ["llms"] }), sitemap = pageDiscoveryPlan({
5839
+ ...options,
5840
+ kinds: ["sitemap-md", "sitemap"]
5841
+ }), related = relatedMarkdownPages({
5090
5842
  context: options.context,
5091
5843
  page: options.page
5092
5844
  }), continueItems = [
@@ -5097,9 +5849,8 @@ function markdownArtifactEnrichment(options) {
5097
5849
  return `${markdownLinkForPage(options.manifest, page)}${description}`;
5098
5850
  }), sectionItems = [
5099
5851
  group?.path ? `[${group.title} home](${canonicalUrl(options.manifest, group.path, options.page.group)})` : void 0,
5100
- sectionLlms ? `[Section LLMS index](${canonicalUrl(options.manifest, sectionLlms, options.page.group)})` : void 0,
5101
- `[Site LLMS index](${canonicalUrl(options.manifest, "/llms.txt")})`,
5102
- `[Sitemap](${canonicalUrl(options.manifest, "/sitemap.md")})`
5852
+ llms ? `[LLMS index](${canonicalUrl(options.manifest, llms.path, llms.group)})` : void 0,
5853
+ sitemap ? `[Sitemap](${canonicalUrl(options.manifest, sitemap.path, sitemap.group)})` : void 0
5103
5854
  ].filter((item) => !!item);
5104
5855
  return [
5105
5856
  markdownListSection("Continue reading", continueItems),
@@ -5108,7 +5859,10 @@ function markdownArtifactEnrichment(options) {
5108
5859
  ].filter((section) => !!section);
5109
5860
  }
5110
5861
  function markdownArtifactBody(options) {
5111
- let markdownPath = markdownRoutePathForPage(options.page.path), group = options.manifest.groups[options.page.group], sectionLlms = groupLlmsPath(options.page.group, options.outputPlans), frontmatter = [
5862
+ let markdownPath = markdownRoutePathForPage(options.page.path), group = options.manifest.groups[options.page.group], llms = pageDiscoveryPlan({ ...options, kinds: ["llms"] }), sitemap = pageDiscoveryPlan({
5863
+ ...options,
5864
+ kinds: ["sitemap-md", "sitemap"]
5865
+ }), frontmatter = [
5112
5866
  "---",
5113
5867
  `title: ${yamlString(options.page.title)}`,
5114
5868
  options.page.description ? `description: ${yamlString(options.page.description)}` : void 0,
@@ -5122,19 +5876,14 @@ function markdownArtifactBody(options) {
5122
5876
  options.page.group,
5123
5877
  options.page.title
5124
5878
  )}`,
5125
- `> Sitemap: ${canonicalUrl(options.manifest, "/sitemap.md")}`,
5126
- `> LLMS index: ${canonicalUrl(options.manifest, "/llms.txt")}`,
5127
- sectionLlms ? `> Section LLMS index: ${canonicalUrl(options.manifest, sectionLlms)}` : void 0
5128
- ].filter(Boolean), body = convertMdxToMarkdown({
5129
- collectDiagnostics: !1,
5130
- mediaAssets: options.mediaManifest.assets,
5131
- source: options.page.body,
5132
- unknownComponentMode: "children"
5133
- }).markdown.trim(), enrichment = markdownArtifactEnrichment({
5879
+ sitemap ? `> Sitemap: ${canonicalUrl(options.manifest, sitemap.path, sitemap.group)}` : void 0,
5880
+ llms ? `> LLMS index: ${canonicalUrl(options.manifest, llms.path, llms.group)}` : void 0
5881
+ ].filter(Boolean), body = options.conversions.get(options.page.path)?.markdown.trim() ?? "", enrichment = markdownArtifactEnrichment({
5134
5882
  manifest: options.manifest,
5135
5883
  outputPlans: options.outputPlans,
5136
5884
  page: options.page,
5137
- context: options.enrichmentContext
5885
+ context: options.enrichmentContext,
5886
+ rootOwnedGroups: options.rootOwnedGroups
5138
5887
  });
5139
5888
  return [
5140
5889
  frontmatter.join(`
@@ -5149,15 +5898,16 @@ function markdownArtifactBody(options) {
5149
5898
 
5150
5899
  `);
5151
5900
  }
5152
- function markdownArtifacts(manifest, outputPlans, mediaManifest) {
5901
+ function markdownArtifacts(manifest, outputPlans, conversions, rootOwnedGroups) {
5153
5902
  let enrichmentContext = createMarkdownEnrichmentContext(manifest);
5154
5903
  return enrichmentContext.publishedPages.map((page) => {
5155
5904
  let body = markdownArtifactBody({
5156
5905
  manifest,
5157
- mediaManifest,
5158
5906
  outputPlans,
5159
5907
  page,
5160
- enrichmentContext
5908
+ conversions,
5909
+ enrichmentContext,
5910
+ rootOwnedGroups
5161
5911
  }), markdownPath = markdownRoutePathForPage(page.path);
5162
5912
  return {
5163
5913
  bodyHash: markdownContentHash(body),
@@ -5431,12 +6181,8 @@ function outputLimit(defaults, declaration) {
5431
6181
  return declaration.limit ?? defaults?.limit;
5432
6182
  }
5433
6183
  function planGeneratedOutputs(config, manifest) {
5434
- let plans = /* @__PURE__ */ new Map(), add = (plan, options = {}) => {
5435
- let normalizedPath = normalizeOutputPath(plan.path);
5436
- if (options.replaceKind)
5437
- for (let [existingPath, existing2] of plans)
5438
- !existing2.group && existing2.kind === plan.kind && plans.delete(existingPath);
5439
- let origin = effectiveOrigin(manifest, plan.group), key = JSON.stringify([origin, normalizedPath]), existing = plans.get(key);
6184
+ let plans = /* @__PURE__ */ new Map(), add = (plan) => {
6185
+ let normalizedPath = normalizeOutputPath(plan.path), origin = effectiveOrigin(manifest, plan.group), key = JSON.stringify([origin, normalizedPath]), existing = plans.get(key);
5440
6186
  if (existing)
5441
6187
  throw new AppError({
5442
6188
  code: "FRAMEWORK_OUTPUT_PATH_COLLISION",
@@ -5448,32 +6194,45 @@ function planGeneratedOutputs(config, manifest) {
5448
6194
  kind: plan.kind
5449
6195
  }
5450
6196
  });
5451
- plans.set(key, { ...plan, path: normalizedPath });
5452
- }, rootDefaults = resolveOutputDefaults(config), rootLlmsDefaults = rootDefaults.llms;
5453
- add({ kind: "robots", path: "/robots.txt" }), add({ kind: "sitemap", path: "/sitemap.xml" }), add({ kind: "sitemap-md", path: "/sitemap.md" }), add({
6197
+ let normalized = { ...plan, path: normalizedPath };
6198
+ return plans.set(key, normalized), normalized;
6199
+ }, rootDefaults = resolveOutputDefaults(config), rootMountedGroup = Object.values(config.groups).find(
6200
+ (group) => group.key !== ROOT_CONTENT_GROUP_KEY && group.path === "/" && effectiveOrigin(manifest, group.key) === manifest.site.origin
6201
+ ), rootMountedOutputs = rootMountedGroup?.outputs, rootLlmsDefaults = resolveOutputDefaults(config, rootMountedGroup).llms, rootScopeGroup = rootMountedGroup?.key ?? ROOT_CONTENT_GROUP_KEY, rootOutputs = {
6202
+ ...config.outputs ?? {},
6203
+ robots: config.outputs?.robots ?? rootMountedOutputs?.robots,
6204
+ sitemap: config.outputs?.sitemap ?? rootMountedOutputs?.sitemap,
6205
+ sitemapMarkdown: config.outputs?.sitemapMarkdown ?? rootMountedOutputs?.sitemapMarkdown,
6206
+ llms: config.outputs?.llms ?? rootMountedOutputs?.llms,
6207
+ llmsFull: config.outputs?.llmsFull ?? rootMountedOutputs?.llmsFull
6208
+ };
6209
+ if (rootOutputs.robots !== !1 && add({ kind: "robots", path: rootOutputs.robots?.path ?? "/robots.txt" }), rootOutputs.sitemap !== !1 && add({
6210
+ kind: "sitemap",
6211
+ path: rootOutputs.sitemap?.path ?? "/sitemap.xml",
6212
+ ...rootOutputs.sitemap?.mode === "urls" ? {} : { sitemaps: [] }
6213
+ }), rootOutputs.sitemapMarkdown !== !1 && add({
6214
+ kind: "sitemap-md",
6215
+ path: rootOutputs.sitemapMarkdown?.path ?? "/sitemap.md",
6216
+ scopeGroup: rootScopeGroup,
6217
+ scope: rootOutputs.sitemapMarkdown?.scope ?? (config.outputs?.sitemapMarkdown === void 0 && rootMountedOutputs?.sitemapMarkdown ? "group" : "site")
6218
+ }), rootOutputs.llms !== !1 && add({
5454
6219
  kind: "llms",
5455
- path: "/llms.txt",
5456
- scope: rootLlmsDefaults?.scope ?? "site",
5457
- limit: rootLlmsDefaults?.limit,
6220
+ path: rootOutputs.llms?.path ?? "/llms.txt",
6221
+ scopeGroup: rootScopeGroup,
6222
+ scope: outputScope(
6223
+ rootLlmsDefaults,
6224
+ rootOutputs.llms ?? {},
6225
+ config.outputs?.llms === void 0 && rootMountedOutputs?.llms ? "group" : "site"
6226
+ ),
6227
+ limit: outputLimit(rootLlmsDefaults, rootOutputs.llms ?? {}),
5458
6228
  includeDescriptions: rootLlmsDefaults?.includeDescriptions
5459
- }), add({ kind: "llms-full", path: "/llms-full.txt" });
5460
- let rootOutputs = config.outputs ?? {};
5461
- if (rootOutputs.robots && add(
5462
- { kind: "robots", path: rootOutputs.robots.path },
5463
- { replaceKind: !0 }
5464
- ), rootOutputs.sitemap && add(
5465
- { kind: "sitemap", path: rootOutputs.sitemap.path },
5466
- { replaceKind: !0 }
5467
- ), rootOutputs.llms && add(
5468
- {
5469
- kind: "llms",
5470
- path: rootOutputs.llms.path,
5471
- scope: outputScope(rootLlmsDefaults, rootOutputs.llms, "site"),
5472
- limit: outputLimit(rootLlmsDefaults, rootOutputs.llms),
5473
- includeDescriptions: rootLlmsDefaults?.includeDescriptions
5474
- },
5475
- { replaceKind: !0 }
5476
- ), rootOutputs.feeds?.rss) {
6229
+ }), rootOutputs.llmsFull !== !1 && add({
6230
+ kind: "llms-full",
6231
+ path: rootOutputs.llmsFull?.path ?? "/llms-full.txt",
6232
+ scopeGroup: rootScopeGroup,
6233
+ scope: rootOutputs.llmsFull?.scope ?? (config.outputs?.llmsFull === void 0 && rootMountedOutputs?.llmsFull ? "group" : "site"),
6234
+ limit: rootOutputs.llmsFull?.limit
6235
+ }), rootOutputs.feeds?.rss) {
5477
6236
  let defaults = feedDefaults(rootDefaults, "rss");
5478
6237
  add({
5479
6238
  kind: "rss",
@@ -5504,23 +6263,56 @@ function planGeneratedOutputs(config, manifest) {
5504
6263
  });
5505
6264
  }
5506
6265
  for (let group of Object.values(config.groups)) {
5507
- let defaults = resolveOutputDefaults(config, group);
5508
- if (group.outputs?.sitemap && add({
5509
- kind: "sitemap",
5510
- path: joinOutputPath(group.path, group.outputs.sitemap.path),
5511
- group: group.key
5512
- }), group.outputs?.llms) {
6266
+ let defaults = resolveOutputDefaults(config, group), nested = group.key !== ROOT_CONTENT_GROUP_KEY && group.path !== "/";
6267
+ 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({
6279
+ kind: "sitemap",
6280
+ path: joinOutputPath(
6281
+ group.path,
6282
+ group.outputs?.sitemap ? group.outputs.sitemap.path ?? "sitemap.xml" : "sitemap.xml"
6283
+ ),
6284
+ group: group.key
6285
+ });
6286
+ }
6287
+ if (group.key !== rootMountedGroup?.key && (group.outputs?.llms || nested && group.outputs?.llms !== !1)) {
5513
6288
  let llmsDefaults = defaults.llms;
5514
6289
  add({
5515
6290
  kind: "llms",
5516
- path: joinOutputPath(group.path, group.outputs.llms.path),
6291
+ path: joinOutputPath(
6292
+ group.path,
6293
+ group.outputs?.llms ? group.outputs.llms.path : "llms.txt"
6294
+ ),
5517
6295
  group: group.key,
5518
- scope: outputScope(llmsDefaults, group.outputs.llms, "group"),
5519
- limit: outputLimit(llmsDefaults, group.outputs.llms),
6296
+ scope: outputScope(llmsDefaults, group.outputs?.llms || {}, "group"),
6297
+ limit: outputLimit(llmsDefaults, group.outputs?.llms || {}),
5520
6298
  includeDescriptions: llmsDefaults?.includeDescriptions
5521
6299
  });
5522
6300
  }
5523
- if (group.outputs?.feeds?.rss) {
6301
+ if (group.key !== rootMountedGroup?.key && (group.outputs?.sitemapMarkdown || nested && group.outputs?.sitemapMarkdown !== !1) && add({
6302
+ kind: "sitemap-md",
6303
+ path: joinOutputPath(
6304
+ group.path,
6305
+ group.outputs?.sitemapMarkdown ? group.outputs.sitemapMarkdown.path : "sitemap.md"
6306
+ ),
6307
+ group: group.key,
6308
+ scope: group.outputs?.sitemapMarkdown ? group.outputs.sitemapMarkdown.scope ?? "group" : "group"
6309
+ }), group.key !== rootMountedGroup?.key && group.outputs?.llmsFull && add({
6310
+ kind: "llms-full",
6311
+ path: joinOutputPath(group.path, group.outputs.llmsFull.path),
6312
+ group: group.key,
6313
+ scope: group.outputs.llmsFull.scope ?? "group",
6314
+ limit: group.outputs.llmsFull.limit
6315
+ }), group.outputs?.feeds?.rss) {
5524
6316
  let feedOptions = feedDefaults(defaults, "rss");
5525
6317
  add({
5526
6318
  kind: "rss",
@@ -5554,19 +6346,98 @@ function planGeneratedOutputs(config, manifest) {
5554
6346
  });
5555
6347
  }
5556
6348
  }
6349
+ let xmlLeaves = [...plans.values()].filter(
6350
+ (plan) => plan.kind === "sitemap" && plan.group
6351
+ ), rootSitemap = [...plans.values()].find(
6352
+ (plan) => plan.kind === "sitemap" && !plan.group
6353
+ );
6354
+ 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 ([
6359
+ "/",
6360
+ ...Object.values(manifest.groups).filter(
6361
+ (group) => effectiveOrigin(manifest, group.key) === manifest.site.origin
6362
+ ).map((group) => group.path),
6363
+ ...manifest.pages.filter(
6364
+ (page) => page.status === "published" && effectiveOrigin(manifest, page.group) === manifest.site.origin
6365
+ ).map((page) => page.path)
6366
+ ].some(
6367
+ (path) => path.startsWith(directory) && !leaves.some((leaf) => path.startsWith(outputDirectory2(leaf.path)))
6368
+ )) {
6369
+ let rootLeaf = add({
6370
+ kind: "sitemap",
6371
+ path: `${rootSitemap.path.replace(/\.[^/.]+$/, "")}-pages.xml`
6372
+ });
6373
+ leaves.push(rootLeaf);
6374
+ }
6375
+ if (rootSitemap.sitemaps = leaves.map(({ path, group }) => ({
6376
+ path,
6377
+ ...group ? { group } : {}
6378
+ })), !rootSitemap.sitemaps.length)
6379
+ throw new AppError({
6380
+ code: "FRAMEWORK_OUTPUT_PATH_INVALID",
6381
+ message: `Sitemap index "${rootSitemap.path}" has no generated sitemap files within its directory.`,
6382
+ fix: "Put the root sitemap index at the origin root, or use a directory that contains an enabled group sitemap.",
6383
+ details: { path: rootSitemap.path, directory }
6384
+ });
6385
+ xmlLeaves.push(...leaves.filter((leaf) => !leaf.group));
6386
+ }
6387
+ 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 } : {} }));
6392
+ }
5557
6393
  let groupLlmsPlans = [...plans.values()].filter(
5558
- (plan) => plan.kind === "llms" && plan.group && effectiveOrigin(manifest, plan.group) === manifest.site.origin
6394
+ (plan) => plan.kind === "llms" && plan.group
5559
6395
  );
5560
6396
  for (let plan of plans.values())
5561
- plan.kind !== "llms" || plan.group || !groupLlmsPlans.length || (plan.links = groupLlmsPlans.map((llmsPlan) => ({
6397
+ plan.kind === "llms" ? (plan.links = groupLlmsPlans.filter(
6398
+ (candidate) => candidate !== plan && discoveryPlanCovers({
6399
+ manifest,
6400
+ plan,
6401
+ group: candidate.group ?? "",
6402
+ path: manifest.groups[candidate.group ?? ""]?.path ?? "/"
6403
+ })
6404
+ ).map((llmsPlan) => ({
5562
6405
  title: config.groups[llmsPlan.group ?? ""]?.title ?? llmsPlan.group ?? "Group",
5563
6406
  path: llmsPlan.path,
5564
6407
  group: llmsPlan.group
5565
- })));
6408
+ })), plan.group && (plan.otherGroupLinks = groupLlmsPlans.filter(
6409
+ (candidate) => candidate !== plan && candidate.group !== plan.group && effectiveOrigin(manifest, candidate.group) === effectiveOrigin(manifest, plan.group) && !discoveryPlanCovers({
6410
+ manifest,
6411
+ plan,
6412
+ group: candidate.group ?? "",
6413
+ path: manifest.groups[candidate.group ?? ""]?.path ?? "/"
6414
+ })
6415
+ ).map((candidate) => ({
6416
+ title: `${config.groups[candidate.group ?? ""]?.title ?? candidate.group ?? "Group"} llms.txt`,
6417
+ path: candidate.path,
6418
+ group: candidate.group
6419
+ })))) : plan.kind === "sitemap-md" && (plan.markdownAlternates = config.agent?.enabled !== !1 && config.agent?.markdownRoutes !== !1, plan.links = [...plans.values()].filter(
6420
+ (candidate) => (candidate.kind === "llms" || candidate.kind === "llms-full" || candidate.kind === "sitemap" && (plan.group ? candidate.group === plan.group : candidate === rootSitemap)) && candidate.group === plan.group
6421
+ ).map((candidate) => ({
6422
+ title: candidate.kind === "llms" ? "LLMS index" : candidate.kind === "llms-full" ? "Full LLMS index" : "XML sitemap",
6423
+ path: candidate.path,
6424
+ ...candidate.group ? { group: candidate.group } : {}
6425
+ })), plan.group && (plan.otherGroupLinks = [...plans.values()].filter(
6426
+ (candidate) => (candidate.kind === "sitemap-md" || candidate.kind === "sitemap" || candidate.kind === "llms") && candidate.group && candidate.group !== plan.group && effectiveOrigin(manifest, candidate.group) === effectiveOrigin(manifest, plan.group) && !discoveryPlanCovers({
6427
+ manifest,
6428
+ plan,
6429
+ group: candidate.group,
6430
+ path: manifest.groups[candidate.group]?.path ?? "/"
6431
+ })
6432
+ ).map((candidate) => ({
6433
+ title: `${config.groups[candidate.group ?? ""]?.title ?? candidate.group ?? "Group"} ${candidate.kind === "sitemap-md" ? "sitemap.md" : candidate.kind === "sitemap" ? "XML sitemap" : "llms.txt"}`,
6434
+ path: candidate.path,
6435
+ group: candidate.group
6436
+ }))));
5566
6437
  return [...plans.values()].sort((a, b) => a.path.localeCompare(b.path));
5567
6438
  }
5568
6439
  function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest) {
5569
- let outputKey = (origin, path) => JSON.stringify([origin, normalizeOutputPath(path)]), reserved = /* @__PURE__ */ new Map(), reserve = (origin, path, value) => {
6440
+ let outputKey = (origin, path) => JSON.stringify([origin, path]), reserved = /* @__PURE__ */ new Map(), reserve = (origin, path, value) => {
5570
6441
  let key = outputKey(origin, path);
5571
6442
  reserved.has(key) || reserved.set(key, value);
5572
6443
  };
@@ -5588,10 +6459,7 @@ function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest) {
5588
6459
  ]);
5589
6460
  for (let asset of Object.values(mediaManifest?.assets ?? {}))
5590
6461
  for (let origin of origins)
5591
- reserve(origin, asset.publicPath, {
5592
- kind: "media",
5593
- owner: asset.sourcePath
5594
- }), reserve(origin, asset.outputPath, {
6462
+ reserve(origin, asset.outputPath, {
5595
6463
  kind: "media",
5596
6464
  owner: asset.sourcePath
5597
6465
  });
@@ -5634,7 +6502,7 @@ function validateGeneratedOutputPaths(config, manifest, plans, mediaManifest) {
5634
6502
  outputRoutes.set(routePath, { path: plan.path, origin });
5635
6503
  }
5636
6504
  }
5637
- async function appFilePlans(config, manifest, fonts, groupThemes, options) {
6505
+ async function appFilePlans(config, manifest, fonts, groupThemes, privateFiles = [], notFoundScopes = []) {
5638
6506
  let hasRootMountedGroup = Object.values(config.groups).some(
5639
6507
  (group) => group.key !== ROOT_CONTENT_GROUP_KEY && group.path === "/"
5640
6508
  ), plans = [
@@ -5652,6 +6520,25 @@ async function appFilePlans(config, manifest, fonts, groupThemes, options) {
5652
6520
  path: "app/providers.tsx",
5653
6521
  content: BANNER + await readFile6(resolve7(SHIMS_DIR, "providers.tsx"), "utf-8")
5654
6522
  },
6523
+ {
6524
+ path: "app/document.tsx",
6525
+ content: BANNER + await readFile6(resolve7(SHIMS_DIR, "document.tsx"), "utf8")
6526
+ },
6527
+ {
6528
+ path: "app/metadata-icons.tsx",
6529
+ content: BANNER + await readFile6(resolve7(SHIMS_DIR, "metadata-icons.tsx"), "utf8")
6530
+ },
6531
+ {
6532
+ path: "app/not-found-content.tsx",
6533
+ content: BANNER + await readFile6(resolve7(SHIMS_DIR, "not-found-content.tsx"), "utf8")
6534
+ },
6535
+ {
6536
+ path: "app/not-found-navigation.tsx",
6537
+ content: BANNER + await readFile6(
6538
+ resolve7(SHIMS_DIR, "not-found-navigation.tsx"),
6539
+ "utf8"
6540
+ )
6541
+ },
5655
6542
  {
5656
6543
  path: "proxy.ts",
5657
6544
  content: BANNER + (await readFile6(resolve7(SHIMS_DIR, "proxy.ts"), "utf-8")).replaceAll(
@@ -5669,7 +6556,39 @@ async function appFilePlans(config, manifest, fonts, groupThemes, options) {
5669
6556
  "utf-8"
5670
6557
  )
5671
6558
  }
5672
- ], outputPlans = planGeneratedOutputs(config, manifest);
6559
+ ], notFoundLayoutImports = [], notFoundLayouts = [];
6560
+ for (let [index, scope] of notFoundScopes.entries()) {
6561
+ if (!scope.group) continue;
6562
+ let groupChain = await resolveLayoutChain(config, scope.group, {
6563
+ themedGroups: groupThemes
6564
+ }), modulePath = `not-found-layout-${index}`;
6565
+ notFoundLayoutImports.push(
6566
+ `import NotFoundLayout${index} from "@app/${modulePath}";`
6567
+ ), notFoundLayouts.push(
6568
+ `${JSON.stringify(scope.id)}: NotFoundLayout${index},`
6569
+ ), plans.push({
6570
+ path: `app/${modulePath}.tsx`,
6571
+ content: BANNER + await generatedGroupLayoutModule(
6572
+ config,
6573
+ scope.group,
6574
+ groupChain,
6575
+ groupThemes,
6576
+ fonts
6577
+ )
6578
+ });
6579
+ }
6580
+ plans.push({
6581
+ path: "app/global-not-found.tsx",
6582
+ content: BANNER + await renderShim("global-not-found.tsx", {
6583
+ __PUBLISHOS_NOT_FOUND_LAYOUT_IMPORTS__: notFoundLayoutImports.join(`
6584
+ `),
6585
+ __PUBLISHOS_NOT_FOUND_LAYOUTS__: `{
6586
+ ${notFoundLayouts.join(`
6587
+ `)}
6588
+ }`
6589
+ })
6590
+ });
6591
+ let outputPlans = planGeneratedOutputs(config, manifest);
5673
6592
  validateGeneratedOutputPaths(config, manifest, outputPlans);
5674
6593
  for (let output of outputPlans)
5675
6594
  plans.push({
@@ -5682,25 +6601,7 @@ async function appFilePlans(config, manifest, fonts, groupThemes, options) {
5682
6601
  }), plans.push({
5683
6602
  path: `app/${FRAMEWORK_PREFIX.slice(1)}/api/og/[key]/route.ts`,
5684
6603
  content: BANNER + await readFile6(resolve7(SHIMS_DIR, "og-image-route.ts"), "utf-8")
5685
- }), options.includeDirectDesignSystemRoute && (plans.push({
5686
- path: "app/ds/page.tsx",
5687
- content: BANNER + await readFile6(
5688
- resolve7(DIRECT_SHIMS_DIR, "design-system-page.tsx"),
5689
- "utf-8"
5690
- )
5691
- }), plans.push({
5692
- path: "app/ds/progress/page.tsx",
5693
- content: BANNER + await readFile6(
5694
- resolve7(DIRECT_SHIMS_DIR, "design-system-progress-page.tsx"),
5695
- "utf-8"
5696
- )
5697
- }), plans.push({
5698
- path: "app/ds/links/page.tsx",
5699
- content: BANNER + await readFile6(
5700
- resolve7(DIRECT_SHIMS_DIR, "design-system-links-page.tsx"),
5701
- "utf-8"
5702
- )
5703
- }));
6604
+ });
5704
6605
  for (let [groupKey, group] of Object.entries(config.groups)) {
5705
6606
  let routeDir = routeDirForGroup(config, group), groupChain = await resolveLayoutChain(config, groupKey, {
5706
6607
  themedGroups: groupThemes
@@ -5748,7 +6649,14 @@ async function appFilePlans(config, manifest, fonts, groupThemes, options) {
5748
6649
  })
5749
6650
  }));
5750
6651
  }
5751
- return plans;
6652
+ let occupied = new Set(plans.map((plan) => plan.path)), collision = privateFiles.find((file) => occupied.has(file.path));
6653
+ if (collision)
6654
+ throw new AppError({
6655
+ code: "FRAMEWORK_PRIVATE_COMPOSITION_CONFLICT",
6656
+ message: "A private app file conflicts with a generated framework route.",
6657
+ details: { path: collision.path }
6658
+ });
6659
+ return [...plans, ...privateFiles];
5752
6660
  }
5753
6661
  function withPublicNavigationTargets(nodes, manifest) {
5754
6662
  return nodes.map((node) => node.type === "page" ? {
@@ -5765,8 +6673,13 @@ async function writeComposedApplication(options) {
5765
6673
  let preparedSource = options.preparedSource, config = preparedSource.config, manifest = withAttribution(preparedSource.manifest, {
5766
6674
  config,
5767
6675
  projectSlug: options.projectSlug
5768
- }), connectedOrigin = options.hostedSite ? normalizeHostedSiteUrl(options.hostedSite.url) : void 0;
5769
- options.hostedSite && (manifest.site.name = config.site?.name?.trim() || normalizeHostedSiteName(options.hostedSite.name)), manifest.site.origin = options.previewUrl ?? connectedOrigin;
6676
+ }), connectedOrigin = options.hostedSite ? normalizeHostedSiteUrl(options.hostedSite.url) : void 0, rootOwnedGroups = new Set(
6677
+ Object.keys(manifest.groups).filter((key) => {
6678
+ let publicOrigin = config.groups[key]?.origin ?? config.origin ?? connectedOrigin;
6679
+ return !connectedOrigin || !publicOrigin || publicOrigin === connectedOrigin;
6680
+ })
6681
+ );
6682
+ options.hostedSite && (manifest.site.name = config.site?.name?.trim() || normalizeHostedSiteName(options.hostedSite.name)), manifest.site.origin = options.previewUrl ?? connectedOrigin ?? config.origin;
5770
6683
  for (let [groupKey, group] of Object.entries(manifest.groups))
5771
6684
  group.origin = options.previewUrl ? options.previewUrl : options.hostedSite ? config.groups[groupKey]?.origin ?? (groupKey === ROOT_CONTENT_GROUP_KEY ? void 0 : config.origin) ?? connectedOrigin : void 0;
5772
6685
  manifest.tree = withPublicNavigationTargets(manifest.tree, manifest);
@@ -5781,19 +6694,33 @@ async function writeComposedApplication(options) {
5781
6694
  },
5782
6695
  signal: options.signal,
5783
6696
  onDownloadStart: options.onAssetDownloadStart
6697
+ }), warnedMediaFiles = new Set(
6698
+ media.warnings.flatMap(
6699
+ (warning) => typeof warning.details?.file == "string" ? [warning.details.file] : []
6700
+ )
6701
+ ), resourceAdmission = createThemeResourceAdmission({
6702
+ media,
6703
+ publishOsDir: options.publishOsDir
5784
6704
  }), theme = await createThemeBuildPlan({
5785
6705
  config,
5786
- publishOsDir: options.publishOsDir
6706
+ media,
6707
+ publishOsDir: options.publishOsDir,
6708
+ resourceAdmission,
6709
+ onWarning: (warning) => {
6710
+ warning.file && warnedMediaFiles.has(warning.file) || (warning.file && warnedMediaFiles.add(warning.file), options.onWarning?.(warning));
6711
+ }
5787
6712
  }), fonts = await fontBuildPlan({
5788
6713
  config,
5789
6714
  publishOsDir: options.publishOsDir,
5790
6715
  prepareAssets,
5791
- themeGroups: theme.groups
6716
+ themeGroups: theme.groups,
6717
+ resourceAdmission
5792
6718
  }), og = await buildOgManifest({
5793
6719
  faviconsAndLogos,
5794
6720
  fonts: fonts.og,
5795
6721
  prepareFonts: prepareAssets,
5796
6722
  manifest,
6723
+ media,
5797
6724
  mediaManifest: media.manifest,
5798
6725
  publishOsDir: options.publishOsDir,
5799
6726
  themes: theme.og,
@@ -5801,19 +6728,26 @@ async function writeComposedApplication(options) {
5801
6728
  code: "FRAMEWORK_OG_COLOR_FALLBACK",
5802
6729
  message
5803
6730
  })
5804
- }), sourceDir = resolve7(options.outputDir, "source"), includeDirectDesignSystemRoute = await hasDirectDesignSystemRoute(), outputPlans = planGeneratedOutputs(config, manifest);
6731
+ }), sourceDir = resolve7(options.outputDir, "source"), outputPlans = planGeneratedOutputs(config, manifest);
5805
6732
  validateGeneratedOutputPaths(config, manifest, outputPlans, media.manifest);
5806
6733
  let { defaultsUsed, sourceOverrides } = await overlaySource(
5807
6734
  options.publishOsDir,
5808
6735
  sourceDir,
5809
6736
  config,
5810
6737
  fonts,
5811
- theme
6738
+ theme,
6739
+ manifest.pages
5812
6740
  );
5813
- includeDirectDesignSystemRoute && (await writeText(
5814
- resolve7(sourceDir, "_internal", "design-system-preview.generated.tsx"),
5815
- await designSystemPreviewModule(options.publishOsDir)
5816
- ), defaultsUsed.push("_internal/design-system-preview.generated.tsx")), await writeText(
6741
+ for (let file of options.privateComposition?.sourceFiles ?? []) {
6742
+ if (await fileExists5(resolve7(sourceDir, file.path)))
6743
+ throw new AppError({
6744
+ code: "FRAMEWORK_PRIVATE_COMPOSITION_CONFLICT",
6745
+ message: "A private source file conflicts with an existing source file.",
6746
+ details: { path: file.path }
6747
+ });
6748
+ await writeText(resolve7(sourceDir, file.path), BANNER + file.content);
6749
+ }
6750
+ await writeText(
5817
6751
  resolve7(sourceDir, ".publishos", "media-manifest.generated.ts"),
5818
6752
  mediaManifestModule(media.manifest)
5819
6753
  ), await writeText(
@@ -5834,23 +6768,38 @@ async function writeComposedApplication(options) {
5834
6768
  resolve7(options.outputDir, ".meta", "og-manifest.json"),
5835
6769
  `${JSON.stringify(ogManifestMetadata(og), null, 2)}
5836
6770
  `
5837
- ), await writeText(
6771
+ );
6772
+ let pageArtifactConversions = convertPublishedPageBodies(manifest), markdownRouteArtifacts = markdownArtifacts(
6773
+ manifest,
6774
+ outputPlans,
6775
+ pageArtifactConversions,
6776
+ rootOwnedGroups
6777
+ ), markdownPaths = config.agent?.enabled === !1 || config.agent?.markdownRoutes === !1 ? /* @__PURE__ */ new Set() : new Set(
6778
+ markdownRouteArtifacts.map((artifact) => artifact.markdownPath)
6779
+ ), notFoundScopes = createNotFoundScopes({
6780
+ manifest,
6781
+ outputs: outputPlans,
6782
+ rootOwnedGroups,
6783
+ markdownPaths
6784
+ });
6785
+ await writeText(
5838
6786
  resolve7(sourceDir, ".publishos", "agent-route-config.generated.ts"),
5839
6787
  agentRouteConfigModule(
5840
6788
  config,
5841
6789
  outputPlans,
5842
6790
  manifest,
5843
- includeDirectDesignSystemRoute ? ["/ds", "/ds/progress", "/ds/links"] : []
6791
+ options.privateComposition?.publicPaths ?? [],
6792
+ notFoundScopes,
6793
+ markdownRouteArtifacts.map((artifact) => artifact.htmlPath)
5844
6794
  )
5845
- );
5846
- let markdownRouteArtifacts = markdownArtifacts(
5847
- manifest,
5848
- outputPlans,
5849
- media.manifest
5850
- );
5851
- await writeText(
6795
+ ), await writeText(
5852
6796
  resolve7(sourceDir, ".publishos", "agent-text.generated.ts"),
5853
- agentTextOutputsModule(manifest, outputPlans, markdownRouteArtifacts)
6797
+ agentTextOutputsModule(
6798
+ manifest,
6799
+ outputPlans,
6800
+ markdownRouteArtifacts,
6801
+ markdownPaths
6802
+ )
5854
6803
  );
5855
6804
  let markdownRouteArtifactShards = createMarkdownArtifactShards(
5856
6805
  markdownRouteArtifacts
@@ -5875,13 +6824,18 @@ async function writeComposedApplication(options) {
5875
6824
  2
5876
6825
  )}
5877
6826
  `
5878
- ), await writeMediaOutputs({ media, outputDir: options.outputDir }), await writeFaviconsAndLogosOutputs({
5879
- faviconsAndLogos,
5880
- outputDir: options.outputDir
5881
- });
5882
- let appPlans = await appFilePlans(config, manifest, fonts, theme.groups, {
5883
- includeDirectDesignSystemRoute
5884
- }), componentManifest = await createComponentManifest({
6827
+ ), await writeMediaOutputs({ media, outputDir: options.outputDir });
6828
+ let appPlans = await appFilePlans(
6829
+ config,
6830
+ manifest,
6831
+ fonts,
6832
+ theme.groups,
6833
+ (options.privateComposition?.appFiles ?? []).map((file) => ({
6834
+ ...file,
6835
+ content: BANNER + file.content
6836
+ })),
6837
+ notFoundScopes
6838
+ ), componentManifest = await createComponentManifest({
5885
6839
  applicationFiles: appPlans,
5886
6840
  runtimeDistDir: RUNTIME_DIST_DIR,
5887
6841
  sourceDir,
@@ -5903,13 +6857,20 @@ async function writeComposedApplication(options) {
5903
6857
  });
5904
6858
  for (let plan of appPlans)
5905
6859
  await writeText(join6(options.outputDir, plan.path), plan.content);
6860
+ await mapLocalAssetUrlsInCss(options.outputDir);
5906
6861
  let projectDir = resolve7(DEFAULTS_DIR, "project"), nextConfigTemplate = await readFile6(
5907
6862
  resolve7(projectDir, "next.config.mjs"),
5908
6863
  "utf-8"
5909
6864
  );
5910
6865
  await writeText(
5911
6866
  resolve7(options.outputDir, "next.config.mjs"),
5912
- renderFrameworkNextConfig(nextConfigTemplate)
6867
+ renderFrameworkNextConfig(
6868
+ nextConfigTemplate,
6869
+ await componentResolveAliases({
6870
+ outputDir: options.outputDir,
6871
+ sourceDir
6872
+ })
6873
+ )
5913
6874
  ), await copyText(
5914
6875
  resolve7(projectDir, "tsconfig.json.template"),
5915
6876
  resolve7(options.outputDir, "tsconfig.json")
@@ -5928,6 +6889,7 @@ async function writeComposedApplication(options) {
5928
6889
  resolve7(options.outputDir, "manifest.generated.ts"),
5929
6890
  manifestModule(manifest)
5930
6891
  ), await writeSearchRecordsArtifact({
6892
+ conversions: pageArtifactConversions,
5931
6893
  outputDir: options.outputDir,
5932
6894
  manifest
5933
6895
  }), await writeText(
@@ -6002,7 +6964,7 @@ function normalizeHostedSiteUrl(value) {
6002
6964
  }
6003
6965
  async function physicalOutputPath(path) {
6004
6966
  try {
6005
- if ((await lstat2(path)).isSymbolicLink())
6967
+ if ((await lstat3(path)).isSymbolicLink())
6006
6968
  throw new AppError({
6007
6969
  code: "FRAMEWORK_OUTPUT_SYMLINK",
6008
6970
  message: "The composition output must not be a symbolic link."
@@ -6047,7 +7009,7 @@ function localPreviewOrigin(request, hosted) {
6047
7009
  return value.origin;
6048
7010
  }
6049
7011
  async function removeTemporaryFontCache(staging) {
6050
- let temporaryCache = join6(staging, ".font-assets"), cache = await lstat2(temporaryCache).catch(
7012
+ let temporaryCache = join6(staging, ".font-assets"), cache = await lstat3(temporaryCache).catch(
6051
7013
  (error) => {
6052
7014
  if (error.code !== "ENOENT") throw error;
6053
7015
  }
@@ -6085,7 +7047,21 @@ async function cleanupCompositionStage(staging, onProgress) {
6085
7047
  }
6086
7048
  }
6087
7049
  async function composeFrameworkSource(options) {
6088
- let { request, source, signal, onProgress } = options, outputDir = request.paths.output, archive = request.context?.archive;
7050
+ let { request, source, signal, onProgress } = options, outputDir = request.paths.output, archive = request.context?.archive, privateComposition = request.context?.privateComposition;
7051
+ if (privateComposition) {
7052
+ let validPath = (path, prefix) => path.startsWith(prefix) && !path.includes("\\") && !path.split("/").some((part) => part === "" || part === "." || part === "..");
7053
+ if (!["dev", "compose"].includes(request.operation) || request.context?.framework?.kind !== "checkout" || !privateComposition.appFiles.every(
7054
+ (file) => validPath(file.path, "app/")
7055
+ ) || !privateComposition.sourceFiles.every(
7056
+ (file) => validPath(file.path, "_internal/")
7057
+ ) || !privateComposition.publicPaths.every(
7058
+ (path) => path.startsWith("/") && !path.includes("..")
7059
+ ))
7060
+ throw new AppError({
7061
+ code: "FRAMEWORK_PRIVATE_COMPOSITION_INVALID",
7062
+ message: "Private app files are allowed only for local checkout Dev and must use safe app and source paths."
7063
+ });
7064
+ }
6089
7065
  if (!outputDir || !isAbsolute2(outputDir) || !archive && request.context?.framework?.kind !== "checkout")
6090
7066
  throw new AppError({
6091
7067
  code: "FRAMEWORK_COMPOSE_INPUT_INVALID",
@@ -6124,6 +7100,7 @@ async function composeFrameworkSource(options) {
6124
7100
  try {
6125
7101
  onProgress?.("Composing source"), await writeComposedApplication({
6126
7102
  checkoutRoot: request.context?.framework?.kind === "checkout" ? installedFramework2.packageRoot : void 0,
7103
+ privateComposition,
6127
7104
  publishOsDir: request.paths.source,
6128
7105
  outputDir: staging,
6129
7106
  preparedSource: source,
@@ -6147,8 +7124,6 @@ async function composeFrameworkSource(options) {
6147
7124
  }
6148
7125
 
6149
7126
  export {
6150
- prepareAppInstallation,
6151
- materializeAppInstallation,
6152
7127
  createIdeSourceArtifacts,
6153
7128
  composeFrameworkSource
6154
7129
  };