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

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 (200) hide show
  1. package/compiler-assets/nextjs/defaults/app/globals.css +1 -0
  2. package/compiler-assets/nextjs/defaults/group/index.tsx +50 -0
  3. package/compiler-assets/nextjs/defaults/group/layout.tsx +7 -3
  4. package/compiler-assets/nextjs/defaults/group/page.tsx +35 -43
  5. package/compiler-assets/nextjs/defaults/project/next.config.mjs +22 -11
  6. package/compiler-assets/nextjs/defaults/project/tsconfig.json.template +1 -4
  7. package/compiler-assets/nextjs/defaults/root/css/animation.css +131 -0
  8. package/compiler-assets/nextjs/defaults/root/css/components/dialog.css +4 -1
  9. package/compiler-assets/nextjs/defaults/root/css/components/menu.css +1 -1
  10. package/compiler-assets/nextjs/defaults/root/css/components/navigation-menu.css +1 -1
  11. package/compiler-assets/nextjs/defaults/root/css/components/popover.css +1 -1
  12. package/compiler-assets/nextjs/defaults/root/css/components/select.css +1 -1
  13. package/compiler-assets/nextjs/defaults/root/css/components/tooltip.css +1 -1
  14. package/compiler-assets/nextjs/defaults/root/css/shadow.css +6 -25
  15. package/compiler-assets/nextjs/defaults/root/css/typography.css +51 -68
  16. package/compiler-assets/nextjs/defaults/root/globals.css +25 -0
  17. package/compiler-assets/nextjs/defaults/root/{page.tsx → index.tsx} +6 -2
  18. package/compiler-assets/nextjs/defaults/root/layout.tsx +7 -1
  19. package/compiler-assets/nextjs/defaults/root/theme.css +22 -22
  20. package/compiler-assets/nextjs/shims/generated-group-page.tsx +19 -2
  21. package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +16 -2
  22. package/compiler-assets/nextjs/shims/global-layout.tsx +11 -43
  23. package/compiler-assets/nextjs/shims/proxy.ts +21 -9
  24. package/compiler-assets/nextjs/shims/publish-os-theme-provider.tsx +76 -15
  25. package/compiler-assets/nextjs/shims/root-page.tsx +7 -1
  26. package/compiler-assets/nextjs/shims/site-fenced-code-block.tsx +34 -0
  27. package/compiler-assets/nextjs/shims/site-heading-level.tsx +14 -0
  28. package/compiler-assets/nextjs/shims/site-mdx-components.tsx +100 -0
  29. package/compiler-assets/nextjs/shims/site-mdx.tsx +12 -0
  30. package/compiler-assets/nextjs/shims/theme-defaults.css +41 -25
  31. package/dist/build-operation-2JAYBSKF.js +83 -0
  32. package/dist/button-CtzUJB8u.d.ts +11 -0
  33. package/dist/{button-CFdx-ohV.d.ts → button-c3WGnxIj.d.ts} +3 -3
  34. package/dist/chunk-535TM4A5.js +1455 -0
  35. package/dist/chunk-726GV2NF.js +2344 -0
  36. package/dist/chunk-7OGTPSKI.js +156 -0
  37. package/dist/chunk-AI2UULNX.js +9 -0
  38. package/dist/chunk-B2V22WRT.js +297 -0
  39. package/dist/chunk-DLDFHU7Y.js +191 -0
  40. package/dist/chunk-DWA3ZRGW.js +142 -0
  41. package/dist/chunk-GGKFQ3MA.js +621 -0
  42. package/dist/chunk-H6XILMZX.js +6154 -0
  43. package/dist/chunk-NECL5IBJ.js +318 -0
  44. package/dist/chunk-SVYGXXYR.js +36 -0
  45. package/dist/chunk-UF6ATARM.js +1100 -0
  46. package/dist/chunk-VO2I7MTM.js +56 -0
  47. package/dist/chunk-WDY4IK6Z.js +128 -0
  48. package/dist/chunk-WKTEO6RS.js +586 -0
  49. package/dist/chunk-YLTOLYWM.js +92 -0
  50. package/dist/compiler.d.ts +41 -7
  51. package/dist/compiler.js +48 -13143
  52. package/dist/compose-NN23UJGC.js +13 -0
  53. package/dist/config/createNextConfig.js +13 -0
  54. package/dist/dev-operation-TZDVJCN7.js +1055 -0
  55. package/dist/framework-selection-X4U6Y6QC.js +10 -0
  56. package/dist/ide-support-ZNW7ODFY.js +15 -0
  57. package/dist/index.d.ts +15 -3
  58. package/dist/kernel/components/blocks/AgentActions.js +71 -61
  59. package/dist/kernel/components/blocks/design-system-preview.d.ts +1 -1
  60. package/dist/kernel/components/blocks/design-system-preview.js +32 -13
  61. package/dist/kernel/components/blocks/link-playground.js +2 -2
  62. package/dist/kernel/components/blocks/logo-block.js +2 -2
  63. package/dist/kernel/components/blocks/nav-breadcrumb.d.ts +10 -2
  64. package/dist/kernel/components/blocks/nav-breadcrumb.js +20 -5
  65. package/dist/kernel/components/blocks/navbar.d.ts +2 -2
  66. package/dist/kernel/components/blocks/navbar.js +401 -60
  67. package/dist/kernel/components/blocks/navigation-progress-playground.js +4 -4
  68. package/dist/kernel/components/blocks/navigation-progress.js +4 -1
  69. package/dist/kernel/components/blocks/next-prev.d.ts +1 -1
  70. package/dist/kernel/components/blocks/next-prev.js +11 -23
  71. package/dist/kernel/components/blocks/page-list.d.ts +1 -1
  72. package/dist/kernel/components/blocks/page-list.js +5 -4
  73. package/dist/kernel/components/blocks/search.d.ts +83 -58
  74. package/dist/kernel/components/blocks/search.js +553 -426
  75. package/dist/kernel/components/blocks/sidebar-node.d.ts +2 -2
  76. package/dist/kernel/components/blocks/sidebar-node.js +11 -9
  77. package/dist/kernel/components/blocks/sidebar.d.ts +3 -3
  78. package/dist/kernel/components/blocks/sidebar.js +18 -11
  79. package/dist/kernel/components/blocks/theme-toggle.d.ts +2 -2
  80. package/dist/kernel/components/blocks/theme-toggle.js +48 -2
  81. package/dist/kernel/components/blocks/toc.js +3 -3
  82. package/dist/kernel/components/mdx/components/blockquote.js +1 -1
  83. package/dist/kernel/components/mdx/components/callout.d.ts +5 -7
  84. package/dist/kernel/components/mdx/components/callout.js +66 -35
  85. package/dist/kernel/components/mdx/components/code-block.d.ts +5 -2
  86. package/dist/kernel/components/mdx/components/code-block.js +106 -5
  87. package/dist/kernel/components/mdx/components/copy-code-button.d.ts +8 -0
  88. package/dist/kernel/components/mdx/components/copy-code-button.js +60 -0
  89. package/dist/kernel/components/mdx/components/figure-caption.js +1 -1
  90. package/dist/kernel/components/mdx/components/figure.js +1 -1
  91. package/dist/kernel/components/mdx/components/heading-copy-link.js +3 -3
  92. package/dist/kernel/components/mdx/components/image-zoom.js +1 -1
  93. package/dist/kernel/components/mdx/components/image.js +77 -56
  94. package/dist/kernel/components/mdx/components/inline-code.js +1 -1
  95. package/dist/kernel/components/mdx/components/kbd.d.ts +2 -2
  96. package/dist/kernel/components/mdx/components/kbd.js +3 -2
  97. package/dist/kernel/components/mdx/components/link.d.ts +35 -4
  98. package/dist/kernel/components/mdx/components/link.js +1 -1
  99. package/dist/kernel/components/mdx/components/paragraph.js +1 -4
  100. package/dist/kernel/components/mdx/components/task-list.js +1 -1
  101. package/dist/kernel/components/mdx/components/video-player.js +2 -2
  102. package/dist/kernel/components/mdx/components/video.js +1 -1
  103. package/dist/kernel/components/mdx/error-boundary.d.ts +1 -1
  104. package/dist/kernel/components/mdx/mdx.d.ts +12 -8
  105. package/dist/kernel/components/mdx/mdx.js +115 -39
  106. package/dist/kernel/components/mdx/rehype-heading-ids.js +1 -1
  107. package/dist/kernel/components/mdx/types.d.ts +1 -1
  108. package/dist/kernel/components/ui/breadcrumb-trail.d.ts +6 -23
  109. package/dist/kernel/components/ui/breadcrumb.d.ts +11 -3
  110. package/dist/kernel/components/ui/breadcrumb.js +6 -4
  111. package/dist/kernel/components/ui/button-group.d.ts +1 -1
  112. package/dist/kernel/components/ui/button-group.js +5 -4
  113. package/dist/kernel/components/ui/button.d.ts +2 -2
  114. package/dist/kernel/components/ui/button.js +6 -6
  115. package/dist/kernel/components/ui/card.d.ts +2 -2
  116. package/dist/kernel/components/ui/drawer.d.ts +3 -1
  117. package/dist/kernel/components/ui/drawer.js +5 -1
  118. package/dist/kernel/components/ui/input.d.ts +4 -4
  119. package/dist/kernel/components/ui/kbd.d.ts +1 -1
  120. package/dist/kernel/components/ui/kbd.js +9 -16
  121. package/dist/kernel/components/ui/link.d.ts +5 -3
  122. package/dist/kernel/components/ui/link.js +10 -9
  123. package/dist/kernel/components/ui/menu.js +5 -5
  124. package/dist/kernel/components/ui/mod-key.d.ts +10 -0
  125. package/dist/kernel/components/ui/mod-key.js +36 -0
  126. package/dist/kernel/components/ui/navigation-menu.js +2 -2
  127. package/dist/kernel/components/ui/progress.js +1 -1
  128. package/dist/kernel/components/ui/scroll-area.d.ts +8 -8
  129. package/dist/kernel/components/ui/scroll-area.js +4 -4
  130. package/dist/kernel/components/ui/select.js +7 -10
  131. package/dist/kernel/components/ui/text.d.ts +2 -2
  132. package/dist/kernel/components/ui/theme-toggle.d.ts +3 -3
  133. package/dist/kernel/components/ui/theme-toggle.js +4 -9
  134. package/dist/kernel/defaults.registry.d.ts +9 -9
  135. package/dist/kernel/defaults.registry.js +9 -9
  136. package/dist/kernel/routes/agent/markdown.d.ts +0 -1
  137. package/dist/kernel/routes/layouts/group.d.ts +1 -1
  138. package/dist/kernel/routes/metadata/feed.d.ts +1 -1
  139. package/dist/kernel/routes/metadata/feed.js +13 -9
  140. package/dist/kernel/routes/metadata/llms.d.ts +2 -1
  141. package/dist/kernel/routes/metadata/llms.js +4 -4
  142. package/dist/kernel/routes/metadata/robots.d.ts +1 -1
  143. package/dist/kernel/routes/metadata/robots.js +2 -2
  144. package/dist/kernel/routes/metadata/sitemap.d.ts +6 -3
  145. package/dist/kernel/routes/metadata/sitemap.js +48 -20
  146. package/dist/kernel/routes/pages/group.d.ts +1 -1
  147. package/dist/kernel/routes/pages/group.js +20 -12
  148. package/dist/kernel/routes/pages/leaf.d.ts +2 -2
  149. package/dist/kernel/routes/pages/leaf.js +20 -12
  150. package/dist/kernel/routes/pages/root.d.ts +2 -2
  151. package/dist/kernel/routes/pages/root.js +18 -12
  152. package/dist/resolvers/manifest-resolver.js +29 -6
  153. package/dist/search.d.ts +6 -7
  154. package/dist/search.js +47 -22
  155. package/dist/{sidebar-types-Bgchdfd6.d.ts → sidebar-types-DKpR27II.d.ts} +1 -1
  156. package/dist/{types-CIwwieNw.d.ts → types-BzkZty3r.d.ts} +23 -3
  157. package/dist/{types-DHBm7-j6.d.ts → types-CJWRG05y.d.ts} +20 -1
  158. package/dist/{types-B_b3C0U6.d.ts → types-CUtrppxt.d.ts} +8 -2
  159. package/dist/types.d.ts +14 -6
  160. package/dist/validate-local-source-XRL2Y7XE.js +14 -0
  161. package/dist/validate-publish-os-source-DAPZ4QGE.js +12 -0
  162. package/init/seed/minimal/.gitignore +1 -0
  163. package/init/seed/minimal/publish.os/assets/favicon/favicon.dark.png +0 -0
  164. package/init/seed/minimal/publish.os/assets/favicon/favicon.png +0 -0
  165. package/init/seed/minimal/publish.os/docs/content/get-started.mdx +6 -10
  166. package/init/seed/minimal/publish.os/docs/content/groups.mdx +1 -1
  167. package/init/seed/minimal/publish.os/docs/content/{theme.mdx → themes.mdx} +14 -12
  168. package/init/seed/minimal/publish.os/publish.os.config.json +3 -7
  169. package/init/seed/minimal/publish.os/theme.css +52 -24
  170. package/init/seed/minimal/publish.os/tsconfig.json +3 -0
  171. package/init/seed/minimal/seed.json +4 -1
  172. package/init/seed/scratch/publish.os/docs/content/get-started.mdx +6 -0
  173. package/init/seed/scratch/publish.os/publish.os.config.json +4 -0
  174. package/init/seed/scratch/seed.json +5 -0
  175. package/package.json +12 -26
  176. package/release/app/phosphor/package.json +3 -3
  177. package/release/app/phosphor/pnpm-lock.yaml.template +523 -360
  178. package/release/app/phosphor/pnpm-workspace.yaml +3 -0
  179. package/release/compiler/package.json +1 -1
  180. package/release/compiler/pnpm-lock.yaml.template +439 -286
  181. package/release/compiler/pnpm-workspace.yaml +3 -0
  182. package/release/release.json +7 -36
  183. package/compiler-assets/nextjs/defaults/group/leaf.tsx +0 -48
  184. package/dist/icons.d.ts +0 -1
  185. package/dist/icons.js +0 -2
  186. package/dist/kernel/components/mdx/components/pre.d.ts +0 -6
  187. package/dist/kernel/components/mdx/components/pre.js +0 -18
  188. package/dist/kernel/components/ui/meta-key.d.ts +0 -17
  189. package/dist/kernel/components/ui/meta-key.js +0 -9
  190. package/dist/kernel/framework-icons.d.ts +0 -1
  191. package/dist/kernel/framework-icons.js +0 -39
  192. package/dist/link-CYaCRDb6.d.ts +0 -35
  193. package/dist/route-metadata-og-image-BeKKFHg7.d.ts +0 -58
  194. package/dist/types-sM2g9nW0.d.ts +0 -12
  195. package/release/app/lucide/package.json +0 -30
  196. package/release/app/lucide/pnpm-lock.yaml.template +0 -2905
  197. package/release/app/lucide/pnpm-workspace.yaml +0 -11
  198. package/release/app/tabler/package.json +0 -30
  199. package/release/app/tabler/pnpm-lock.yaml.template +0 -2911
  200. package/release/app/tabler/pnpm-workspace.yaml +0 -11
@@ -0,0 +1,2344 @@
1
+ import {
2
+ supportedNextjsSourceImports
3
+ } from "./chunk-NECL5IBJ.js";
4
+ import {
5
+ ROOT_CONTENT_GROUP_KEY,
6
+ ROOT_CONTENT_GROUP_TITLE,
7
+ assertSourceDependency,
8
+ buildFaviconsAndLogos,
9
+ buildMediaAssets,
10
+ collectBindingNames,
11
+ groupedMdxExports,
12
+ joinUrlPath,
13
+ localCssImports,
14
+ mdxComponentExportName,
15
+ namedValueExports,
16
+ normalizeSlugPath,
17
+ normalizeUrlPath,
18
+ replacedMdxSourceFiles
19
+ } from "./chunk-535TM4A5.js";
20
+ import {
21
+ AppError as AppError2,
22
+ ERROR_CODES,
23
+ assertPublishOsSourceBoundary
24
+ } from "./chunk-7OGTPSKI.js";
25
+ import {
26
+ isApprovedPublishOsPath,
27
+ isExcludedSourcePath,
28
+ isFrameworkNamespacePath,
29
+ isReservedFrameworkPath,
30
+ isSameOrDescendant
31
+ } from "./chunk-YLTOLYWM.js";
32
+ import {
33
+ runtimePackageVersion
34
+ } from "./chunk-AI2UULNX.js";
35
+ import {
36
+ AppError,
37
+ readInstalledFrameworkLocation
38
+ } from "./chunk-WDY4IK6Z.js";
39
+
40
+ // src/lib/compiler/validation/validate-publish-os-source.ts
41
+ import { readdir as readdir3, readFile as readFile5, stat as stat3 } from "fs/promises";
42
+ import { basename, dirname, extname as extname3, relative as relative4, resolve as resolve7, sep } from "path";
43
+ import ts2 from "typescript";
44
+
45
+ // src/lib/compiler/source/diagnostics/diagnostic-store.ts
46
+ import { z } from "zod";
47
+ var diagnosticSchema = z.object({
48
+ code: z.string(),
49
+ level: z.union([z.literal("error"), z.literal("warning")]),
50
+ message: z.string(),
51
+ details: z.record(z.unknown()).optional()
52
+ }), DiagnosticStore = class {
53
+ diagnostics = [];
54
+ add(diagnostic) {
55
+ let parsed = diagnosticSchema.parse(diagnostic);
56
+ this.diagnostics.push(parsed);
57
+ }
58
+ error(code, message, details) {
59
+ this.add({ code, level: "error", message, details });
60
+ }
61
+ warning(code, message, details) {
62
+ this.add({ code, level: "warning", message, details });
63
+ }
64
+ all() {
65
+ return [...this.diagnostics];
66
+ }
67
+ hasErrors() {
68
+ return this.diagnostics.some((d) => d.level === "error");
69
+ }
70
+ };
71
+
72
+ // src/lib/compiler/source/icon-source-contract.ts
73
+ function findLegacyIconResolver(source) {
74
+ return /<Icon\b/.exec(source);
75
+ }
76
+
77
+ // src/lib/compiler/source/prepare-publish-os-source.ts
78
+ import { resolve as resolve5 } from "path";
79
+
80
+ // src/lib/compiler/source/config/resolve-publish-os-config.ts
81
+ import { resolve as resolve3 } from "path";
82
+
83
+ // src/lib/compiler/source/config/load-publish-os-config.ts
84
+ import { readFile } from "fs/promises";
85
+ import { resolve } from "path";
86
+
87
+ // src/lib/compiler/source/config/format-config-issues.ts
88
+ function issueMessages(issue) {
89
+ return issue.code === "invalid_union" ? issue.unionErrors.flatMap(
90
+ (error) => error.issues.flatMap(issueMessages)
91
+ ) : issue.code === "unrecognized_keys" ? issue.keys.map(
92
+ (key) => `${[...issue.path, key].join(".")}: Unknown field.`
93
+ ) : [`${issue.path.join(".") || "(root)"}: ${issue.message}`];
94
+ }
95
+ function formatConfigIssues(error) {
96
+ return [...new Set(error.issues.flatMap(issueMessages))].join(`
97
+ `);
98
+ }
99
+
100
+ // src/lib/compiler/source/config/publish-os-config-schema.ts
101
+ import { z as z4 } from "zod";
102
+
103
+ // src/lib/compiler/source/config/og-metadata-schema.ts
104
+ import { z as z2 } from "zod";
105
+ var ogImageSchema = z2.union([
106
+ z2.string().min(1),
107
+ z2.object({ src: z2.string().min(1), alt: z2.string().optional() }).strict()
108
+ ]), ogIndexMetadataSchema = z2.object({
109
+ title: z2.string().optional(),
110
+ description: z2.string().optional(),
111
+ image: ogImageSchema.optional(),
112
+ scope: z2.enum(["page", "subtree"]).optional()
113
+ }).strict().refine((value) => value.scope === void 0 || value.image !== void 0, {
114
+ message: "Set og.image when you set og.scope.",
115
+ path: ["scope"]
116
+ });
117
+
118
+ // src/lib/compiler/source/config/publish-os-group-schema.ts
119
+ import { z as z3 } from "zod";
120
+ var treeNodeSchema = z3.lazy(
121
+ () => z3.union([
122
+ z3.object({
123
+ type: z3.literal("section"),
124
+ id: z3.string(),
125
+ title: z3.string(),
126
+ children: z3.array(treeNodeSchema)
127
+ }),
128
+ z3.object({
129
+ type: z3.literal("page"),
130
+ file: z3.string(),
131
+ title: z3.string().optional(),
132
+ slug: z3.string().optional()
133
+ }),
134
+ z3.object({
135
+ type: z3.literal("link"),
136
+ id: z3.string(),
137
+ title: z3.string(),
138
+ url: z3.string()
139
+ }),
140
+ z3.object({
141
+ type: z3.literal("folder"),
142
+ id: z3.string(),
143
+ title: z3.string(),
144
+ group: z3.string().optional(),
145
+ children: z3.array(treeNodeSchema).optional()
146
+ })
147
+ ])
148
+ ), groupListSchema = z3.object({
149
+ source: z3.enum(["content", "ids"]),
150
+ ids: z3.array(z3.string()).optional(),
151
+ sort: z3.object({
152
+ by: z3.string(),
153
+ direction: z3.enum(["asc", "desc"]).optional()
154
+ }).optional(),
155
+ prevNext: z3.boolean().optional()
156
+ }).strict(), outputScopeSchema = z3.enum(["site", "group", "self"]), outputStatusSchema = z3.enum(["published", "draft", "archived"]), outputDeclarationSchema = z3.object({
157
+ path: z3.string().min(1),
158
+ scope: outputScopeSchema.optional(),
159
+ description: z3.string().optional(),
160
+ limit: z3.number().int().positive().optional()
161
+ }).strict(), outputDefaultsDeclarationSchema = z3.object({
162
+ scope: outputScopeSchema.optional(),
163
+ limit: z3.number().int().positive().optional(),
164
+ status: z3.array(outputStatusSchema).optional(),
165
+ includeDescriptions: z3.boolean().optional()
166
+ }).strict(), feedsOutputSchema = z3.object({
167
+ rss: outputDeclarationSchema.optional(),
168
+ atom: outputDeclarationSchema.optional(),
169
+ json: outputDeclarationSchema.optional()
170
+ }).strict(), feedsOutputDefaultsSchema = outputDefaultsDeclarationSchema.extend({
171
+ rss: outputDefaultsDeclarationSchema.optional(),
172
+ atom: outputDefaultsDeclarationSchema.optional(),
173
+ json: outputDefaultsDeclarationSchema.optional()
174
+ }).strict(), outputsSchema = z3.object({
175
+ llms: outputDeclarationSchema.optional(),
176
+ feeds: feedsOutputSchema.optional(),
177
+ robots: outputDeclarationSchema.optional(),
178
+ sitemap: outputDeclarationSchema.optional()
179
+ }).strict(), outputDefaultsSchema = z3.object({
180
+ llms: outputDefaultsDeclarationSchema.optional(),
181
+ feeds: feedsOutputDefaultsSchema.optional()
182
+ }).strict();
183
+ function normalizeHttpsOrigin(value) {
184
+ try {
185
+ let url = new URL(value);
186
+ return url.protocol !== "https:" || url.username || url.password || /[?#]/.test(value) || url.pathname !== "/" ? void 0 : url.origin;
187
+ } catch {
188
+ return;
189
+ }
190
+ }
191
+ var httpsOriginSchema = z3.string().transform((value) => normalizeHttpsOrigin(value) ?? value).refine(
192
+ (value) => value.startsWith("https://") && normalizeHttpsOrigin(value) === value,
193
+ {
194
+ message: "Use an HTTPS origin without a path, query, or fragment."
195
+ }
196
+ ), groupThemeForcedModeSchema = z3.enum(["light", "dark", "none"]), groupThemeSchema = z3.object({
197
+ forcedMode: groupThemeForcedModeSchema.optional()
198
+ }).strict(), staticFontWeightSchema = z3.enum([
199
+ "100",
200
+ "200",
201
+ "300",
202
+ "400",
203
+ "500",
204
+ "600",
205
+ "700",
206
+ "800",
207
+ "900"
208
+ ]), variableFontWeightSchema = z3.union([
209
+ z3.literal("variable"),
210
+ z3.string().regex(/^\d+\s+\d+$/)
211
+ ]), fontWeightSchema = z3.union([
212
+ z3.literal("all"),
213
+ variableFontWeightSchema,
214
+ staticFontWeightSchema,
215
+ z3.array(staticFontWeightSchema)
216
+ ]), localFontStyleSchema = z3.enum(["normal", "italic"]), fontStyleSchema = z3.union([
217
+ localFontStyleSchema,
218
+ z3.tuple([z3.literal("normal"), z3.literal("italic")])
219
+ ]), localFontFileSchema = z3.object({
220
+ path: z3.string().min(1),
221
+ weight: z3.union([variableFontWeightSchema, staticFontWeightSchema]).optional(),
222
+ style: localFontStyleSchema.optional()
223
+ }).strict(), fontRoleSchema = z3.union([
224
+ z3.string().min(1),
225
+ z3.object({
226
+ family: z3.string().min(1),
227
+ src: z3.union([z3.string().min(1), z3.array(localFontFileSchema)]).optional(),
228
+ weight: fontWeightSchema.optional(),
229
+ style: fontStyleSchema.optional(),
230
+ subsets: z3.array(z3.string().min(1)).optional(),
231
+ axes: z3.array(z3.string().min(1)).optional(),
232
+ display: z3.enum(["auto", "block", "swap", "fallback", "optional"]).optional(),
233
+ preload: z3.boolean().optional(),
234
+ fallback: z3.array(z3.string().min(1)).optional(),
235
+ adjustFontFallback: z3.boolean().optional()
236
+ }).strict()
237
+ ]), fontConfigSchema = z3.record(
238
+ z3.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
239
+ fontRoleSchema
240
+ ), groupConfigDocumentSchema = z3.lazy(
241
+ () => z3.object({
242
+ origin: httpsOriginSchema.optional(),
243
+ og: ogIndexMetadataSchema.optional(),
244
+ title: z3.string().optional(),
245
+ slug: z3.string().optional(),
246
+ path: z3.string().regex(/^\//).optional(),
247
+ mode: z3.enum(["tree", "list"]).optional(),
248
+ theme: groupThemeSchema.optional(),
249
+ fonts: fontConfigSchema.optional(),
250
+ meta: z3.record(z3.unknown()).optional(),
251
+ tree: z3.array(treeNodeSchema).optional(),
252
+ list: groupListSchema.optional(),
253
+ outputs: outputsSchema.optional(),
254
+ outputDefaults: outputDefaultsSchema.optional()
255
+ }).strict()
256
+ );
257
+
258
+ // src/lib/compiler/source/config/publish-os-config-schema.ts
259
+ var siteSchema = z4.object({
260
+ name: z4.string().optional(),
261
+ description: z4.string().optional()
262
+ }).strict(), runtimePackageMessage = 'Set runtime to "@publish.os/nextjs@<exact-version>". Replace <exact-version> with the exact framework version for this source.', runtimeSchema = z4.string({
263
+ required_error: runtimePackageMessage,
264
+ invalid_type_error: runtimePackageMessage
265
+ }).refine((value) => runtimePackageVersion(value) !== void 0, {
266
+ message: runtimePackageMessage
267
+ }), rootThemeSchema = z4.object({
268
+ defaultMode: z4.enum(["system", "light", "dark"]).optional(),
269
+ forcedMode: z4.enum(["light", "dark"]).optional()
270
+ }).strict(), remoteMediaSourceSchema = z4.object({
271
+ protocol: z4.literal("https"),
272
+ hostname: z4.string().min(1),
273
+ pathname: z4.string().optional()
274
+ }).strict(), remoteEmbedSourceSchema = z4.object({
275
+ provider: z4.enum([
276
+ "youtube",
277
+ "vimeo",
278
+ "loom",
279
+ "wistia",
280
+ "cloudflare-stream"
281
+ ]),
282
+ hostname: z4.string().min(1)
283
+ }).strict(), mediaConfigSchema = z4.object({
284
+ assets: z4.object({
285
+ maxImageBytes: z4.number().int().positive().optional(),
286
+ maxVideoBytes: z4.number().int().positive().optional(),
287
+ maxTotalBytes: z4.number().int().positive().optional(),
288
+ maxFileCount: z4.number().int().positive().optional()
289
+ }).strict().optional(),
290
+ remote: z4.object({
291
+ images: z4.array(remoteMediaSourceSchema).optional(),
292
+ videos: z4.array(remoteMediaSourceSchema).optional(),
293
+ embeds: z4.array(remoteEmbedSourceSchema).optional()
294
+ }).strict().optional()
295
+ }).strict(), agentConfigSchema = z4.object({
296
+ enabled: z4.boolean().optional(),
297
+ markdownRoutes: z4.boolean().optional(),
298
+ markdownAccept: z4.boolean().optional(),
299
+ plainTextAccept: z4.boolean().optional(),
300
+ agentDetection: z4.boolean().optional(),
301
+ discoveryHeaders: z4.boolean().optional(),
302
+ blockInternalMarkdownRoute: z4.boolean().optional()
303
+ }).strict(), publishOsConfigSchema = z4.object({
304
+ $schema: z4.string().optional(),
305
+ version: z4.literal(1),
306
+ runtime: runtimeSchema,
307
+ origin: httpsOriginSchema.optional(),
308
+ site: siteSchema.optional(),
309
+ agent: agentConfigSchema.optional(),
310
+ theme: rootThemeSchema.optional(),
311
+ fonts: fontConfigSchema.optional(),
312
+ media: mediaConfigSchema.optional(),
313
+ og: ogIndexMetadataSchema.optional(),
314
+ meta: z4.record(z4.unknown()).optional(),
315
+ outputs: outputsSchema.optional(),
316
+ outputDefaults: outputDefaultsSchema.optional(),
317
+ groups: z4.record(groupConfigDocumentSchema).default({})
318
+ }).strict();
319
+
320
+ // src/lib/compiler/source/diagnostics/json-source-location.ts
321
+ import ts from "typescript";
322
+ function atOffset(raw, offset, length) {
323
+ let before = raw.slice(0, offset), line = before.split(/\r?\n/).length, lineStart = Math.max(before.lastIndexOf(`
324
+ `) + 1, 0);
325
+ return {
326
+ line,
327
+ column: offset - lineStart + 1,
328
+ sourceLine: raw.split(/\r?\n/)[line - 1] ?? "",
329
+ length: Math.max(1, length)
330
+ };
331
+ }
332
+ function invalidJsonLocation(raw, error) {
333
+ let diagnostic = ts.parseJsonText("config.json", raw).parseDiagnostics?.[0];
334
+ if (diagnostic?.start !== void 0)
335
+ return atOffset(raw, diagnostic.start, diagnostic.length ?? 1);
336
+ let message = error instanceof Error ? error.message : "", position = /\bposition (\d+)\b/.exec(message)?.[1];
337
+ if (position !== void 0) {
338
+ let offset = Number(position), trailingComma = /,\s*$/.exec(raw.slice(0, offset));
339
+ return atOffset(
340
+ raw,
341
+ trailingComma ? offset - trailingComma[0].length : offset,
342
+ 1
343
+ );
344
+ }
345
+ }
346
+ function jsonValueLocation(raw, path) {
347
+ let source = ts.parseJsonText("config.json", raw), statement = source.statements[0];
348
+ if (!statement || !ts.isExpressionStatement(statement)) return;
349
+ let current = statement.expression;
350
+ for (let part of path) {
351
+ if (typeof part == "number") {
352
+ if (!ts.isArrayLiteralExpression(current)) return;
353
+ let child = current.elements[part];
354
+ if (!child || !ts.isExpression(child)) return;
355
+ current = child;
356
+ continue;
357
+ }
358
+ if (!ts.isObjectLiteralExpression(current)) return;
359
+ let property = current.properties.find(
360
+ (candidate) => ts.isPropertyAssignment(candidate) && (ts.isStringLiteral(candidate.name) || ts.isIdentifier(candidate.name)) && candidate.name.text === part
361
+ );
362
+ if (!property || !ts.isPropertyAssignment(property)) return;
363
+ current = property.initializer;
364
+ }
365
+ return atOffset(raw, current.getStart(source), current.getWidth(source));
366
+ }
367
+
368
+ // src/lib/compiler/source/config/load-publish-os-config.ts
369
+ function parseRootConfig(parsed, raw) {
370
+ if (parsed && typeof parsed == "object" && "icons" in parsed)
371
+ throw new AppError2({
372
+ code: ERROR_CODES.CONFIG_SCHEMA_INVALID,
373
+ message: 'The "icons" configuration was removed. PublishOS now uses Phosphor icons by default.',
374
+ details: {
375
+ file: "publish.os.config.json",
376
+ ...jsonValueLocation(raw, ["icons"]),
377
+ propertyPath: "icons",
378
+ correction: 'Remove "icons" from publish.os.config.json and import icons from "@phosphor-icons/react/ssr".'
379
+ }
380
+ });
381
+ let result = publishOsConfigSchema.safeParse(parsed);
382
+ if (!result.success) {
383
+ let path = result.error.issues[0]?.path.filter(
384
+ (part) => typeof part == "string" || typeof part == "number"
385
+ ) ?? [];
386
+ throw new AppError2({
387
+ code: ERROR_CODES.CONFIG_SCHEMA_INVALID,
388
+ message: `publish.os.config.json failed schema validation
389
+ ${formatConfigIssues(result.error)}`,
390
+ details: {
391
+ file: "publish.os.config.json",
392
+ ...jsonValueLocation(raw, path),
393
+ propertyPath: path.length > 0 ? path.join(".") : "publish.os.config.json",
394
+ correction: "Correct the reported config property and save again.",
395
+ issues: result.error.format()
396
+ }
397
+ });
398
+ }
399
+ return result.data;
400
+ }
401
+ async function loadPublishOsConfig(baseDir) {
402
+ let path = resolve(baseDir, "publish.os.config.json"), raw;
403
+ try {
404
+ raw = await readFile(path, "utf-8");
405
+ } catch (error) {
406
+ throw typeof error == "object" && error !== null && "code" in error && error.code === "ENOENT" ? new AppError2({
407
+ code: ERROR_CODES.CONFIG_NOT_FOUND,
408
+ message: "publish.os.config.json is required",
409
+ details: { path }
410
+ }) : error;
411
+ }
412
+ let parsed;
413
+ try {
414
+ parsed = JSON.parse(raw);
415
+ } catch (error) {
416
+ let location = invalidJsonLocation(raw, error);
417
+ throw new AppError2({
418
+ code: ERROR_CODES.CONFIG_INVALID_JSON,
419
+ message: "Invalid JSON in publish.os.config.json.",
420
+ details: {
421
+ file: "publish.os.config.json",
422
+ ...location,
423
+ correction: /,\s*[}\]]/.test(raw) ? "Remove the trailing comma and save again." : "Correct the JSON syntax and save again."
424
+ }
425
+ });
426
+ }
427
+ return parseRootConfig(parsed, raw);
428
+ }
429
+
430
+ // src/lib/compiler/source/config/normalize-publish-os-config.ts
431
+ import { readdir, readFile as readFile2, stat } from "fs/promises";
432
+ import { join, relative, resolve as resolve2 } from "path";
433
+ async function pathIsDirectory(absolutePath) {
434
+ return await stat(absolutePath).then((info) => info.isDirectory()).catch(() => !1);
435
+ }
436
+ async function pathIsFile(absolutePath) {
437
+ return await stat(absolutePath).then((info) => info.isFile()).catch(() => !1);
438
+ }
439
+ async function ensureDirectoryExists(absolutePath, groupKey, label) {
440
+ try {
441
+ if (!(await stat(absolutePath)).isDirectory())
442
+ throw new AppError2({
443
+ code: ERROR_CODES.CONFIG_CONTENT_NOT_DIRECTORY,
444
+ message: `Group ${groupKey} ${label} must be a directory`,
445
+ details: { path: absolutePath }
446
+ });
447
+ } catch (error) {
448
+ throw error instanceof AppError2 ? error : new AppError2({
449
+ code: ERROR_CODES.CONFIG_CONTENT_MISSING,
450
+ message: `Group ${groupKey} ${label} does not exist`,
451
+ details: { error, path: absolutePath }
452
+ });
453
+ }
454
+ }
455
+ async function readGroupConfigFile(filePath, groupKey, sourceRoot) {
456
+ if (!await pathIsFile(filePath)) return;
457
+ let raw = await readFile2(filePath, "utf-8"), parsed;
458
+ try {
459
+ parsed = JSON.parse(raw);
460
+ } catch (error) {
461
+ let location = invalidJsonLocation(raw, error);
462
+ throw new AppError2({
463
+ code: ERROR_CODES.CONFIG_MOUNT_INVALID_JSON,
464
+ message: `Invalid JSON in group config for ${groupKey}.`,
465
+ details: {
466
+ file: relative(sourceRoot, filePath).replace(/\\/g, "/"),
467
+ ...location,
468
+ correction: /,\s*[}\]]/.test(raw) ? "Remove the trailing comma and save again." : "Correct the JSON syntax and save again."
469
+ }
470
+ });
471
+ }
472
+ let result = groupConfigDocumentSchema.safeParse(parsed);
473
+ if (!result.success) {
474
+ let path = result.error.issues[0]?.path.filter(
475
+ (part) => typeof part == "string" || typeof part == "number"
476
+ ) ?? [];
477
+ throw new AppError2({
478
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
479
+ message: `Group config ${filePath} failed schema validation
480
+ ${formatConfigIssues(result.error)}`,
481
+ details: {
482
+ file: relative(sourceRoot, filePath).replace(/\\/g, "/"),
483
+ ...jsonValueLocation(raw, path),
484
+ propertyPath: path.length > 0 ? path.join(".") : `groups.${groupKey}`,
485
+ correction: "Correct the reported group config property and save again.",
486
+ issues: result.error.format()
487
+ }
488
+ });
489
+ }
490
+ return result.data;
491
+ }
492
+ function normalizeGroupSlug(value, groupKey) {
493
+ let result = normalizeSlugPath(value, {
494
+ allowEmpty: groupKey === ROOT_CONTENT_GROUP_KEY
495
+ });
496
+ if (result.errors.length > 0)
497
+ throw new AppError2({
498
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
499
+ message: `Invalid group slug "${value}" for ${groupKey}`,
500
+ details: { errors: result.errors, group: groupKey, slug: value }
501
+ });
502
+ return result.value;
503
+ }
504
+ function normalizeGroupPath(value, groupKey) {
505
+ let result = normalizeUrlPath(value, { allowEmpty: !0 });
506
+ if (result.errors.length > 0 || isFrameworkNamespacePath(result.value))
507
+ throw new AppError2({
508
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
509
+ message: isFrameworkNamespacePath(result.value) ? `Group path "${value}" is reserved for framework services` : `Invalid group path "${value}" for ${groupKey}`,
510
+ details: { errors: result.errors, group: groupKey, path: value }
511
+ });
512
+ return result.value;
513
+ }
514
+ function mergeGroupConfig(rootConfig, localConfig) {
515
+ let root = rootConfig ?? {};
516
+ return {
517
+ ...root,
518
+ ...localConfig,
519
+ meta: {
520
+ ...root.meta ?? {},
521
+ ...localConfig?.meta ?? {}
522
+ },
523
+ theme: {
524
+ ...root.theme ?? {},
525
+ ...localConfig?.theme ?? {}
526
+ },
527
+ fonts: {
528
+ ...root.fonts ?? {},
529
+ ...localConfig?.fonts ?? {}
530
+ },
531
+ outputs: {
532
+ ...root.outputs ?? {},
533
+ ...localConfig?.outputs ?? {}
534
+ },
535
+ outputDefaults: {
536
+ ...root.outputDefaults ?? {},
537
+ ...localConfig?.outputDefaults ?? {},
538
+ feeds: {
539
+ ...root.outputDefaults?.feeds ?? {},
540
+ ...localConfig?.outputDefaults?.feeds ?? {}
541
+ },
542
+ llms: {
543
+ ...root.outputDefaults?.llms ?? {},
544
+ ...localConfig?.outputDefaults?.llms ?? {}
545
+ }
546
+ }
547
+ };
548
+ }
549
+ var RESERVED_ROUTE_CLASS_FOLDERS = /* @__PURE__ */ new Set(["(index)", "(page)"]);
550
+ function stripGroupFolderParens(folderName) {
551
+ return folderName.length > 2 && folderName.startsWith("(") && folderName.endsWith(")") ? {
552
+ name: folderName.slice(1, -1),
553
+ layoutBoundary: !0
554
+ } : { name: folderName, layoutBoundary: !1 };
555
+ }
556
+ function normalizeGroupFolderName(folderName) {
557
+ let stripped = stripGroupFolderParens(folderName);
558
+ return {
559
+ localKey: normalizeGroupSlug(stripped.name, stripped.name),
560
+ layoutBoundary: stripped.layoutBoundary
561
+ };
562
+ }
563
+ async function contentFolderHasMdx(contentPath, sourceRoot) {
564
+ let entries = await readdir(contentPath, { withFileTypes: !0 }).catch(
565
+ () => []
566
+ );
567
+ for (let entry of entries) {
568
+ let childPath = join(contentPath, entry.name);
569
+ if (!isExcludedSourcePath(relative(sourceRoot, childPath))) {
570
+ if (entry.isDirectory()) {
571
+ if (await contentFolderHasMdx(childPath, sourceRoot)) return !0;
572
+ continue;
573
+ }
574
+ if (entry.isFile() && entry.name.toLowerCase().endsWith(".mdx"))
575
+ return !0;
576
+ }
577
+ }
578
+ return !1;
579
+ }
580
+ async function isDiscoverableGroupFolder(folderPath, sourceRoot) {
581
+ if (await pathIsFile(join(folderPath, "index.tsx")) || await pathIsFile(join(folderPath, "(index)", "index.tsx")))
582
+ return !0;
583
+ let contentPath = join(folderPath, "content");
584
+ return await pathIsDirectory(contentPath) ? await contentFolderHasMdx(contentPath, sourceRoot) : !1;
585
+ }
586
+ async function discoverGroups(options) {
587
+ let entries = (await readdir(options.folderPath, { withFileTypes: !0 }).catch(() => [])).filter((entry) => entry.isDirectory()).sort((a, b) => a.name.localeCompare(b.name)), siblingKeys = /* @__PURE__ */ new Map(), groups = [];
588
+ for (let entry of entries) {
589
+ if (RESERVED_ROUTE_CLASS_FOLDERS.has(entry.name)) continue;
590
+ let folderPath = join(options.folderPath, entry.name);
591
+ if (isExcludedSourcePath(relative(options.absoluteBase, folderPath)))
592
+ continue;
593
+ let normalized = normalizeGroupFolderName(entry.name);
594
+ if (!normalized.localKey) continue;
595
+ let existing = siblingKeys.get(normalized.localKey);
596
+ if (existing)
597
+ throw new AppError2({
598
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
599
+ message: `Duplicate group key "${normalized.localKey}" for sibling folders ${existing} and ${entry.name}`,
600
+ details: {
601
+ group: normalized.localKey,
602
+ folders: [existing, entry.name],
603
+ parent: options.parentKey
604
+ }
605
+ });
606
+ if (siblingKeys.set(normalized.localKey, entry.name), !await isDiscoverableGroupFolder(folderPath, options.absoluteBase))
607
+ continue;
608
+ let key = options.parentKey ? `${options.parentKey}/${normalized.localKey}` : normalized.localKey, group = {
609
+ key,
610
+ localKey: normalized.localKey,
611
+ localFolderName: entry.name,
612
+ folderPath,
613
+ parentKey: options.parentKey,
614
+ layoutBoundary: normalized.layoutBoundary,
615
+ hasContent: await pathIsDirectory(join(folderPath, "content"))
616
+ };
617
+ groups.push(group), groups.push(
618
+ ...await discoverGroups({
619
+ absoluteBase: options.absoluteBase,
620
+ folderPath,
621
+ parentKey: key
622
+ })
623
+ );
624
+ }
625
+ return groups;
626
+ }
627
+ function orderDiscoveredGroups(groups, rootGroups) {
628
+ let rootOrder = new Map(
629
+ Object.keys(rootGroups).map((key, index) => [key, index])
630
+ ), fallbackRank = rootOrder.size;
631
+ function rootKey(group) {
632
+ return group.key.split("/")[0] ?? group.key;
633
+ }
634
+ function rootRank(group) {
635
+ return rootOrder.get(rootKey(group)) ?? fallbackRank;
636
+ }
637
+ function isAncestor(left, right) {
638
+ return right.key.startsWith(`${left.key}/`);
639
+ }
640
+ return [...groups].sort((left, right) => {
641
+ let rankDelta = rootRank(left) - rootRank(right);
642
+ return rankDelta !== 0 ? rankDelta : isAncestor(left, right) ? -1 : isAncestor(right, left) ? 1 : left.key.localeCompare(right.key);
643
+ });
644
+ }
645
+ function expectedGroupConfigFilename(group) {
646
+ return `${group.localKey}.config.json`;
647
+ }
648
+ async function assertValidGroupConfigFilenames(group) {
649
+ let expected = expectedGroupConfigFilename(group), entries = await readdir(group.folderPath, {
650
+ withFileTypes: !0
651
+ }).catch(() => []);
652
+ for (let entry of entries)
653
+ if (!(!entry.isFile() || !entry.name.endsWith(".config.json")) && entry.name !== expected)
654
+ throw new AppError2({
655
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
656
+ message: `Group ${group.key} must use ${expected}; ${entry.name} is not supported`,
657
+ details: {
658
+ group: group.key,
659
+ expected,
660
+ path: join(group.folderPath, entry.name)
661
+ }
662
+ });
663
+ }
664
+ function assertValidRootGroupConfigKeys(groups) {
665
+ for (let key of Object.keys(groups)) {
666
+ if (key === ROOT_CONTENT_GROUP_KEY)
667
+ throw new AppError2({
668
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
669
+ message: `Root group config key "${key}" is reserved for PublishOS root content`,
670
+ details: { group: key }
671
+ });
672
+ if (!(!key.includes("(") && !key.includes(")")))
673
+ throw new AppError2({
674
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
675
+ message: `Root group config key "${key}" must use the normalized group key without parentheses`,
676
+ details: { group: key }
677
+ });
678
+ }
679
+ }
680
+ function rootContentGroup(absoluteBase, rootFilePath) {
681
+ let contentPath = join(absoluteBase, "content");
682
+ return {
683
+ key: ROOT_CONTENT_GROUP_KEY,
684
+ title: ROOT_CONTENT_GROUP_TITLE,
685
+ slug: "",
686
+ path: "/",
687
+ contentPath,
688
+ folderPath: absoluteBase,
689
+ localFolderName: "",
690
+ layoutBoundary: !1,
691
+ ancestry: [ROOT_CONTENT_GROUP_KEY],
692
+ __meta: {
693
+ filePath: rootFilePath,
694
+ source: "root",
695
+ basePath: absoluteBase,
696
+ folderPath: absoluteBase,
697
+ localFolderName: "",
698
+ relativeFolderPath: "",
699
+ contentPath,
700
+ relativeContentPath: "content",
701
+ files: {}
702
+ }
703
+ };
704
+ }
705
+ async function collectGroup(options) {
706
+ let {
707
+ absoluteBase,
708
+ discovered,
709
+ parentPath,
710
+ entry,
711
+ rootFilePath,
712
+ groups,
713
+ paths
714
+ } = options, { key, localKey, folderPath, parentKey } = discovered;
715
+ await ensureDirectoryExists(folderPath, key, "folder"), await assertValidGroupConfigFilenames(discovered);
716
+ let localConfigPath = join(
717
+ folderPath,
718
+ expectedGroupConfigFilename(discovered)
719
+ ), localConfig = await readGroupConfigFile(
720
+ localConfigPath,
721
+ key,
722
+ absoluteBase
723
+ ), merged = mergeGroupConfig(entry, localConfig), slug = normalizeGroupSlug(merged.slug ?? localKey, key), path = merged.path ? normalizeGroupPath(merged.path, key) : joinUrlPath(parentPath, slug), contentPath = join(folderPath, "content");
724
+ discovered.hasContent && await ensureDirectoryExists(contentPath, key, "content folder");
725
+ let existingPathOwner = paths.get(path);
726
+ if (existingPathOwner && !(path === "/" && existingPathOwner === ROOT_CONTENT_GROUP_KEY))
727
+ throw new AppError2({
728
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
729
+ message: `Duplicate group path "${path}" for ${key} and ${existingPathOwner}`,
730
+ details: { path, group: key, existingGroup: existingPathOwner }
731
+ });
732
+ paths.set(path, key), groups[key] = {
733
+ ...merged,
734
+ key,
735
+ slug,
736
+ path,
737
+ contentPath,
738
+ folderPath,
739
+ localFolderName: discovered.localFolderName,
740
+ layoutBoundary: discovered.layoutBoundary,
741
+ parentKey,
742
+ ancestry: parentKey ? [...groups[parentKey]?.ancestry ?? [], key] : [key],
743
+ __meta: {
744
+ filePath: localConfig ? localConfigPath : rootFilePath,
745
+ source: localConfig ? "group" : "root",
746
+ basePath: absoluteBase,
747
+ folderPath,
748
+ localFolderName: discovered.localFolderName,
749
+ relativeFolderPath: relative(absoluteBase, folderPath),
750
+ contentPath,
751
+ relativeContentPath: relative(absoluteBase, contentPath),
752
+ files: {}
753
+ }
754
+ };
755
+ }
756
+ async function normalizeConfig(config, baseDir) {
757
+ let absoluteBase = resolve2(baseDir), rootFilePath = resolve2(absoluteBase, "publish.os.config.json"), groups = {}, paths = /* @__PURE__ */ new Map();
758
+ assertValidRootGroupConfigKeys(config.groups);
759
+ let discoveredGroups = orderDiscoveredGroups(
760
+ await discoverGroups({
761
+ absoluteBase,
762
+ folderPath: absoluteBase
763
+ }),
764
+ config.groups
765
+ );
766
+ await contentFolderHasMdx(join(absoluteBase, "content"), absoluteBase) && (groups[ROOT_CONTENT_GROUP_KEY] = rootContentGroup(
767
+ absoluteBase,
768
+ rootFilePath
769
+ ), paths.set("/", ROOT_CONTENT_GROUP_KEY));
770
+ let discoveredKeys = new Set(discoveredGroups.map((group) => group.key));
771
+ for (let key of Object.keys(config.groups))
772
+ if (!discoveredKeys.has(key))
773
+ throw new AppError2({
774
+ code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
775
+ message: `Root group config "${key}" does not match a discovered group`,
776
+ details: { group: key }
777
+ });
778
+ for (let discovered of discoveredGroups) {
779
+ let parentPath = discovered.parentKey ? groups[discovered.parentKey]?.path ?? "/" : "/";
780
+ await collectGroup({
781
+ absoluteBase,
782
+ discovered,
783
+ parentPath,
784
+ entry: config.groups[discovered.key],
785
+ rootFilePath,
786
+ groups,
787
+ paths
788
+ });
789
+ }
790
+ return {
791
+ ...config,
792
+ groups
793
+ };
794
+ }
795
+
796
+ // src/lib/compiler/source/config/resolve-publish-os-config.ts
797
+ async function resolvePublishOsConfig(baseDir) {
798
+ let config = await loadPublishOsConfig(baseDir), absoluteBase = resolve3(baseDir);
799
+ return await normalizeConfig(config, absoluteBase);
800
+ }
801
+
802
+ // src/lib/compiler/source/content/scan-publish-os-content.ts
803
+ import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "fs/promises";
804
+ import { extname, join as join2, relative as relative2 } from "path";
805
+ import matter from "gray-matter";
806
+ var RESERVED_FIELDS = /* @__PURE__ */ new Set([
807
+ "title",
808
+ "slug",
809
+ "description",
810
+ "og",
811
+ "status",
812
+ "publishedAt",
813
+ "updatedAt",
814
+ "authors",
815
+ "tags"
816
+ ]), UNSUPPORTED_FIELDS = /* @__PURE__ */ new Set(["author", "meta", "metadata"]);
817
+ function fileSlug(relativePath) {
818
+ return relativePath.replace(/\\/g, "/").replace(/\.mdx$/i, "");
819
+ }
820
+ function asString(value) {
821
+ return typeof value == "string" && value.length > 0 ? value : void 0;
822
+ }
823
+ function normalizeContentSlug(value, absolutePath) {
824
+ let result = normalizeSlugPath(value);
825
+ if (result.errors.length > 0)
826
+ throw new AppError2({
827
+ code: ERROR_CODES.CONTENT_SLUG_INVALID,
828
+ message: `Invalid content slug "${value}" for ${absolutePath}`,
829
+ details: {
830
+ errors: result.errors,
831
+ path: absolutePath,
832
+ slug: value
833
+ }
834
+ });
835
+ return result.value;
836
+ }
837
+ function asDateString(value) {
838
+ if (typeof value == "string" && value.length > 0) return value;
839
+ if (value instanceof Date && !Number.isNaN(value.getTime()))
840
+ return value.toISOString().slice(0, 10);
841
+ }
842
+ function isIsoDate(value) {
843
+ return !Number.isNaN(Date.parse(value));
844
+ }
845
+ function warning(code, message, details) {
846
+ return { code, level: "warning", message, details };
847
+ }
848
+ function treeSlugs(nodes) {
849
+ function visit(tree) {
850
+ let slugs = /* @__PURE__ */ new Map();
851
+ for (let node of tree)
852
+ if (node.type === "page" && (slugs.has(node.file) || slugs.set(node.file, asString(node.slug))), "children" in node && node.children)
853
+ for (let [fileID, slug] of visit(node.children))
854
+ slug && !slugs.has(fileID) && slugs.set(fileID, slug);
855
+ return slugs;
856
+ }
857
+ return visit(nodes ?? []);
858
+ }
859
+ function normalizeAuthorship(authors, absolutePath) {
860
+ let warnings = [];
861
+ if (authors === void 0)
862
+ return { authors: [], warnings };
863
+ if (!Array.isArray(authors))
864
+ return warnings.push(
865
+ warning(
866
+ "CONTENT_AUTHORS_INVALID",
867
+ `Ignoring invalid authors for ${absolutePath}`,
868
+ {
869
+ path: absolutePath
870
+ }
871
+ )
872
+ ), { authors: [], warnings };
873
+ let normalized = [];
874
+ for (let author of authors) {
875
+ if (typeof author == "string" && author.length > 0) {
876
+ normalized.push({ name: author });
877
+ continue;
878
+ }
879
+ if (author && typeof author == "object" && typeof author.name == "string") {
880
+ let value = author;
881
+ normalized.push({
882
+ id: asString(value.id),
883
+ name: value.name,
884
+ handle: asString(value.handle),
885
+ avatar: asString(value.avatar),
886
+ url: asString(value.url)
887
+ });
888
+ continue;
889
+ }
890
+ warnings.push(
891
+ warning(
892
+ "CONTENT_AUTHOR_INVALID",
893
+ `Ignoring invalid author entry for ${absolutePath}`,
894
+ {
895
+ path: absolutePath
896
+ }
897
+ )
898
+ );
899
+ }
900
+ return { authors: normalized, warnings };
901
+ }
902
+ function normalizeTags(tags, absolutePath) {
903
+ return tags === void 0 ? { tags: [], warnings: [] } : Array.isArray(tags) ? {
904
+ tags: tags.filter((tag) => typeof tag == "string"),
905
+ warnings: []
906
+ } : {
907
+ tags: [],
908
+ warnings: [
909
+ warning(
910
+ "CONTENT_TAGS_INVALID",
911
+ `Ignoring invalid tags for ${absolutePath}`,
912
+ {
913
+ path: absolutePath
914
+ }
915
+ )
916
+ ]
917
+ };
918
+ }
919
+ function normalizeOgMetadata(value, absolutePath) {
920
+ if (value === void 0) return;
921
+ if (!value || typeof value != "object" || Array.isArray(value))
922
+ throw new AppError2({
923
+ code: "CONTENT_FRONTMATTER_OG_INVALID",
924
+ message: `Invalid og frontmatter for ${absolutePath}; expected an object with title, description, or image`,
925
+ details: { path: absolutePath, field: "og" }
926
+ });
927
+ let source = value, allowedFields = /* @__PURE__ */ new Set(["title", "description", "image"]);
928
+ for (let field of Object.keys(source))
929
+ if (!allowedFields.has(field))
930
+ throw new AppError2({
931
+ code: "CONTENT_FRONTMATTER_OG_INVALID",
932
+ message: `Invalid og frontmatter field "${field}" for ${absolutePath}`,
933
+ details: { path: absolutePath, field: `og.${field}` }
934
+ });
935
+ let og = {};
936
+ if (source.title !== void 0) {
937
+ if (typeof source.title != "string" || source.title.length === 0)
938
+ throw new AppError2({
939
+ code: "CONTENT_FRONTMATTER_OG_INVALID",
940
+ message: `Invalid og.title for ${absolutePath}; expected a non-empty string`,
941
+ details: { path: absolutePath, field: "og.title" }
942
+ });
943
+ og.title = source.title;
944
+ }
945
+ if (source.description !== void 0) {
946
+ if (typeof source.description != "string" || source.description.length === 0)
947
+ throw new AppError2({
948
+ code: "CONTENT_FRONTMATTER_OG_INVALID",
949
+ message: `Invalid og.description for ${absolutePath}; expected a non-empty string`,
950
+ details: { path: absolutePath, field: "og.description" }
951
+ });
952
+ og.description = source.description;
953
+ }
954
+ if (source.image !== void 0)
955
+ if (typeof source.image == "string" && source.image.length > 0)
956
+ og.image = source.image;
957
+ else if (source.image && typeof source.image == "object" && !Array.isArray(source.image)) {
958
+ let image = source.image, allowedImageFields = /* @__PURE__ */ new Set(["src", "alt"]);
959
+ for (let field of Object.keys(image))
960
+ if (!allowedImageFields.has(field))
961
+ throw new AppError2({
962
+ code: "CONTENT_FRONTMATTER_OG_INVALID",
963
+ message: `Invalid og.image field "${field}" for ${absolutePath}`,
964
+ details: { path: absolutePath, field: `og.image.${field}` }
965
+ });
966
+ if (typeof image.src != "string" || image.src.length === 0)
967
+ throw new AppError2({
968
+ code: "CONTENT_FRONTMATTER_OG_INVALID",
969
+ message: `Invalid og.image.src for ${absolutePath}; expected a non-empty string`,
970
+ details: { path: absolutePath, field: "og.image.src" }
971
+ });
972
+ if (image.alt !== void 0 && typeof image.alt != "string")
973
+ throw new AppError2({
974
+ code: "CONTENT_FRONTMATTER_OG_INVALID",
975
+ message: `Invalid og.image.alt for ${absolutePath}; expected a string`,
976
+ details: { path: absolutePath, field: "og.image.alt" }
977
+ });
978
+ og.image = {
979
+ src: image.src,
980
+ ...typeof image.alt == "string" ? { alt: image.alt } : {}
981
+ };
982
+ } else
983
+ throw new AppError2({
984
+ code: "CONTENT_FRONTMATTER_OG_INVALID",
985
+ message: `Invalid og.image for ${absolutePath}; expected a non-empty string or object with src`,
986
+ details: { path: absolutePath, field: "og.image" }
987
+ });
988
+ return Object.keys(og).length > 0 ? og : void 0;
989
+ }
990
+ function customMeta(frontmatter, absolutePath) {
991
+ let meta = {}, warnings = [];
992
+ for (let [key, value] of Object.entries(frontmatter))
993
+ if (!RESERVED_FIELDS.has(key)) {
994
+ if (UNSUPPORTED_FIELDS.has(key)) {
995
+ warnings.push(
996
+ warning(
997
+ "CONTENT_FRONTMATTER_FIELD_UNSUPPORTED",
998
+ `Ignoring unsupported frontmatter field "${key}" for ${absolutePath}`,
999
+ { path: absolutePath, field: key }
1000
+ )
1001
+ );
1002
+ continue;
1003
+ }
1004
+ meta[key] = value;
1005
+ }
1006
+ return { meta, warnings };
1007
+ }
1008
+ async function readContentFile(group, absolutePath, configSlugs) {
1009
+ let raw = await readFile3(absolutePath, "utf-8");
1010
+ try {
1011
+ let parsed = matter(raw), frontmatter = parsed.data, relativePath = relative2(group.contentPath, absolutePath), fileID = normalizeContentSlug(fileSlug(relativePath), absolutePath), title = asString(frontmatter.title);
1012
+ if (frontmatter.id !== void 0)
1013
+ throw new AppError2({
1014
+ code: ERROR_CODES.CONTENT_FRONTMATTER_ID_INVALID,
1015
+ message: `Frontmatter field "id" is not supported for ${absolutePath}; page identity is derived from fileID`,
1016
+ details: { path: absolutePath, field: "id" }
1017
+ });
1018
+ if (!fileID)
1019
+ throw new AppError2({
1020
+ code: ERROR_CODES.CONTENT_FRONTMATTER_ID_INVALID,
1021
+ message: `Could not derive fileID for ${absolutePath}`,
1022
+ details: { path: absolutePath }
1023
+ });
1024
+ if (!title)
1025
+ throw new AppError2({
1026
+ code: ERROR_CODES.CONTENT_FRONTMATTER_REQUIRED,
1027
+ message: `Missing required frontmatter field "title" for ${absolutePath}`,
1028
+ details: { path: absolutePath, field: "title" }
1029
+ });
1030
+ let warnings = [], frontmatterSlug = asString(frontmatter.slug), slugSource = configSlugs.get(fileID) ?? frontmatterSlug ?? fileID, slugResult = normalizeSlugPath(slugSource);
1031
+ if (slugResult.errors.length > 0)
1032
+ throw new AppError2({
1033
+ code: ERROR_CODES.CONTENT_SLUG_INVALID,
1034
+ message: `Invalid content slug "${slugSource}" for ${absolutePath}`,
1035
+ details: {
1036
+ errors: slugResult.errors,
1037
+ path: absolutePath,
1038
+ slug: slugSource
1039
+ }
1040
+ });
1041
+ let slug = slugResult.value;
1042
+ slugResult.changed && warnings.push(
1043
+ warning(
1044
+ "CONTENT_SLUG_NORMALIZED",
1045
+ `Normalized slug "${slugSource}" to "${slug}" for ${absolutePath}`,
1046
+ { path: absolutePath, from: slugSource, to: slug }
1047
+ )
1048
+ );
1049
+ let status = "published";
1050
+ frontmatter.status === "published" || frontmatter.status === "draft" || frontmatter.status === "archived" ? status = frontmatter.status : frontmatter.status !== void 0 && warnings.push(
1051
+ warning(
1052
+ "CONTENT_STATUS_INVALID",
1053
+ `Invalid status "${String(frontmatter.status)}" for ${absolutePath}; defaulting to published`,
1054
+ { path: absolutePath, status: frontmatter.status }
1055
+ )
1056
+ );
1057
+ let rawPublishedAt = asDateString(frontmatter.publishedAt), publishedAt = rawPublishedAt && isIsoDate(rawPublishedAt) ? rawPublishedAt : void 0;
1058
+ rawPublishedAt && !publishedAt && warnings.push(
1059
+ warning(
1060
+ "CONTENT_DATE_INVALID",
1061
+ `Invalid publishedAt for ${absolutePath}`,
1062
+ {
1063
+ path: absolutePath,
1064
+ field: "publishedAt",
1065
+ value: rawPublishedAt
1066
+ }
1067
+ )
1068
+ );
1069
+ let rawUpdatedAt = asDateString(frontmatter.updatedAt), updatedAt = rawUpdatedAt && isIsoDate(rawUpdatedAt) ? rawUpdatedAt : void 0;
1070
+ rawUpdatedAt && !updatedAt && warnings.push(
1071
+ warning(
1072
+ "CONTENT_DATE_INVALID",
1073
+ `Invalid updatedAt for ${absolutePath}`,
1074
+ {
1075
+ path: absolutePath,
1076
+ field: "updatedAt",
1077
+ value: rawUpdatedAt
1078
+ }
1079
+ )
1080
+ );
1081
+ let authors = normalizeAuthorship(frontmatter.authors, absolutePath);
1082
+ warnings.push(...authors.warnings);
1083
+ let tags = normalizeTags(frontmatter.tags, absolutePath);
1084
+ warnings.push(...tags.warnings);
1085
+ let og = normalizeOgMetadata(frontmatter.og, absolutePath), meta = customMeta(frontmatter, absolutePath);
1086
+ return warnings.push(...meta.warnings), {
1087
+ fileID,
1088
+ group: group.key,
1089
+ slug,
1090
+ title,
1091
+ description: asString(frontmatter.description),
1092
+ og,
1093
+ status,
1094
+ publishedAt,
1095
+ updatedAt,
1096
+ authors: authors.authors,
1097
+ tags: tags.tags,
1098
+ meta: meta.meta,
1099
+ absolutePath,
1100
+ relativePath,
1101
+ body: parsed.content.trim(),
1102
+ warnings
1103
+ };
1104
+ } catch (error) {
1105
+ throw error instanceof AppError2 ? error : new AppError2({
1106
+ code: ERROR_CODES.CONTENT_FILE_PARSE_FAILED,
1107
+ message: `Failed to parse frontmatter for ${absolutePath}`,
1108
+ details: { error }
1109
+ });
1110
+ }
1111
+ }
1112
+ async function walkContent(group) {
1113
+ let files = [], stack = [group.contentPath], configSlugs = treeSlugs(group.tree);
1114
+ for (; stack.length > 0; ) {
1115
+ let current = stack.pop();
1116
+ if (!current) continue;
1117
+ let entries = await readdir2(current).catch(
1118
+ (error) => {
1119
+ if (error.code === "ENOENT") return [];
1120
+ throw error;
1121
+ }
1122
+ );
1123
+ for (let entry of entries) {
1124
+ let absolute = join2(current, entry);
1125
+ if (isExcludedSourcePath(relative2(group.__meta.basePath, absolute)))
1126
+ continue;
1127
+ if ((await stat2(absolute)).isDirectory()) {
1128
+ stack.push(absolute);
1129
+ continue;
1130
+ }
1131
+ extname(entry).toLowerCase() === ".mdx" && files.push(await readContentFile(group, absolute, configSlugs));
1132
+ }
1133
+ }
1134
+ return files;
1135
+ }
1136
+ function buildRoutes(groups, files) {
1137
+ let routes = [];
1138
+ for (let file of files) {
1139
+ if (file.status === "draft")
1140
+ continue;
1141
+ let group = groups[file.group];
1142
+ if (!group)
1143
+ continue;
1144
+ let path = `${group.path.replace(/\/$/, "")}/${file.slug}`.replace(/\/+/g, "/");
1145
+ routes.push({
1146
+ fileID: file.fileID,
1147
+ slug: file.slug,
1148
+ path: path === "" ? "/" : path,
1149
+ group: file.group
1150
+ });
1151
+ }
1152
+ return routes;
1153
+ }
1154
+ async function scanContent(config) {
1155
+ let files = (await Promise.all(
1156
+ Object.values(config.groups).map((group) => walkContent(group))
1157
+ )).flat(), warnings = files.flatMap((file) => file.warnings), fileIDs = /* @__PURE__ */ new Set();
1158
+ for (let file of files) {
1159
+ let scopedFileID = `${file.group}:${file.fileID}`;
1160
+ if (fileIDs.has(scopedFileID))
1161
+ throw new AppError2({
1162
+ code: ERROR_CODES.CONTENT_DUPLICATE_ID,
1163
+ message: `Duplicate content fileID "${file.fileID}" in group ${file.group}`,
1164
+ details: { fileID: file.fileID, group: file.group }
1165
+ });
1166
+ fileIDs.add(scopedFileID);
1167
+ }
1168
+ let routes = buildRoutes(config.groups, files), routePaths = /* @__PURE__ */ new Set(), groupPaths = new Map(
1169
+ Object.values(config.groups).map((group) => [group.path, group.key])
1170
+ );
1171
+ for (let route of routes) {
1172
+ if (routePaths.has(route.path))
1173
+ throw new AppError2({
1174
+ code: ERROR_CODES.CONTENT_DUPLICATE_ROUTE,
1175
+ message: `Duplicate route "${route.path}"`,
1176
+ details: { route: route.path }
1177
+ });
1178
+ let groupPathOwner = groupPaths.get(route.path);
1179
+ if (groupPathOwner)
1180
+ throw new AppError2({
1181
+ code: ERROR_CODES.CONTENT_DUPLICATE_ROUTE,
1182
+ message: `Content route "${route.path}" collides with group "${groupPathOwner}"`,
1183
+ details: {
1184
+ route: route.path,
1185
+ group: route.group,
1186
+ fileID: route.fileID,
1187
+ existingGroup: groupPathOwner
1188
+ }
1189
+ });
1190
+ routePaths.add(route.path);
1191
+ }
1192
+ let meta = config.meta;
1193
+ return {
1194
+ site: {
1195
+ name: config.site?.name ?? meta?.title ?? "Untitled",
1196
+ description: config.site?.description ?? meta?.description,
1197
+ ...config.og ? { og: config.og } : {}
1198
+ },
1199
+ files,
1200
+ routes,
1201
+ warnings
1202
+ };
1203
+ }
1204
+
1205
+ // src/lib/compiler/source/diagnostics/map-app-error-to-diagnostic.ts
1206
+ function appErrorToDiagnostic(error) {
1207
+ return {
1208
+ code: error.code,
1209
+ level: "error",
1210
+ message: error.message,
1211
+ details: error.details
1212
+ };
1213
+ }
1214
+
1215
+ // src/lib/compiler/source/diagnostics/missing-page-references.ts
1216
+ import { readFile as readFile4 } from "fs/promises";
1217
+ import { relative as relative3, resolve as resolve4 } from "path";
1218
+ async function missingPageReferences(options) {
1219
+ let diagnostics = [], configSource = /* @__PURE__ */ new Map(), routes = new Set(
1220
+ options.content.routes.map((route) => `${route.group}\0${route.fileID}`)
1221
+ ), rootConfig = resolve4(options.sourceRoot, "publish.os.config.json");
1222
+ async function locationFor(groupKey, groupFile, path) {
1223
+ for (let [file, sourcePath] of [
1224
+ [groupFile, path],
1225
+ [rootConfig, ["groups", groupKey, ...path]]
1226
+ ]) {
1227
+ let raw = configSource.get(file);
1228
+ raw === void 0 && (raw = await readFile4(file, "utf8"), configSource.set(file, raw));
1229
+ let location = jsonValueLocation(raw, sourcePath);
1230
+ if (location)
1231
+ return {
1232
+ file: relative3(options.sourceRoot, file).replace(/\\/g, "/"),
1233
+ ...location
1234
+ };
1235
+ }
1236
+ }
1237
+ for (let group of Object.values(options.config.groups)) {
1238
+ async function visit(nodes, path) {
1239
+ for (let [index, node] of nodes.entries()) {
1240
+ let nodePath = [...path, index];
1241
+ if (node.type === "page" && !routes.has(`${group.key}\0${node.file}`)) {
1242
+ let propertyPath = `groups.${group.key}.${nodePath.join(".")}.file`, location = await locationFor(group.key, group.__meta.filePath, [
1243
+ ...nodePath,
1244
+ "file"
1245
+ ]), expected = `publish.os/${group.__meta.relativeContentPath.replace(/\\/g, "/")}/${node.file}.mdx`;
1246
+ diagnostics.push({
1247
+ code: "CONTENT_TREE_PAGE_MISSING",
1248
+ level: "warning",
1249
+ message: `Referenced page "${node.file}" was not found. Expected ${expected}.`,
1250
+ details: {
1251
+ ...location,
1252
+ propertyPath,
1253
+ correction: "Add that page, or change or remove the tree entry."
1254
+ }
1255
+ });
1256
+ }
1257
+ "children" in node && node.children && await visit(node.children, [...nodePath, "children"]);
1258
+ }
1259
+ }
1260
+ await visit(group.tree ?? [], ["tree"]);
1261
+ }
1262
+ return diagnostics;
1263
+ }
1264
+
1265
+ // src/lib/compiler/source/manifest/create-publish-os-manifest.ts
1266
+ import { createHash } from "crypto";
1267
+ function stableStringify(value) {
1268
+ return Array.isArray(value) ? `[${value.map((item) => stableStringify(item)).join(",")}]` : value && typeof value == "object" ? `{${Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(
1269
+ ([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`
1270
+ ).join(",")}}` : JSON.stringify(value);
1271
+ }
1272
+ function resolveGroupReference(config, currentGroup, reference) {
1273
+ let relativeKey = `${currentGroup}/${reference}`;
1274
+ if (config.groups[relativeKey]) return relativeKey;
1275
+ if (config.groups[reference]) return reference;
1276
+ }
1277
+ function normalizeTreeNode(node, group, pagesByFileID, routesByFileID, options) {
1278
+ if (node.type === "page") {
1279
+ let page = pagesByFileID.get(node.file), route = routesByFileID.get(node.file);
1280
+ return !page || !route ? null : {
1281
+ type: "page",
1282
+ fileID: node.file,
1283
+ title: node.title ?? page.title,
1284
+ path: route.path,
1285
+ group,
1286
+ description: page.description,
1287
+ og: page.og,
1288
+ status: page.status,
1289
+ publishedAt: page.publishedAt,
1290
+ updatedAt: page.updatedAt,
1291
+ authors: page.authors,
1292
+ tags: page.tags,
1293
+ meta: page.meta
1294
+ };
1295
+ }
1296
+ if (node.type === "link")
1297
+ return {
1298
+ type: "link",
1299
+ id: node.id,
1300
+ title: node.title,
1301
+ href: node.url
1302
+ };
1303
+ let children = node.children?.map(
1304
+ (child) => normalizeTreeNode(child, group, pagesByFileID, routesByFileID, options)
1305
+ ).filter((child) => child !== null) ?? [];
1306
+ if (node.type === "folder") {
1307
+ let referencedGroup = resolveGroupReference(
1308
+ options.config,
1309
+ group,
1310
+ node.group ?? node.id
1311
+ ), referencedChildren = children.length > 0 || !referencedGroup ? children : options.childTreeForGroup(referencedGroup);
1312
+ return {
1313
+ type: "folder",
1314
+ id: node.id,
1315
+ title: node.title,
1316
+ path: referencedGroup ? options.config.groups[referencedGroup]?.path : void 0,
1317
+ group: referencedGroup ?? group,
1318
+ children: referencedChildren
1319
+ };
1320
+ }
1321
+ return {
1322
+ type: node.type,
1323
+ id: node.id,
1324
+ title: node.title,
1325
+ group,
1326
+ children
1327
+ };
1328
+ }
1329
+ function defaultTree(group, pages, routesByFileID) {
1330
+ return pages.flatMap((page) => {
1331
+ let route = routesByFileID.get(page.fileID);
1332
+ return route ? {
1333
+ type: "page",
1334
+ fileID: page.fileID,
1335
+ title: page.title,
1336
+ path: route.path,
1337
+ group,
1338
+ description: page.description,
1339
+ og: page.og,
1340
+ status: page.status,
1341
+ publishedAt: page.publishedAt,
1342
+ updatedAt: page.updatedAt,
1343
+ authors: page.authors,
1344
+ tags: page.tags,
1345
+ meta: page.meta
1346
+ } : [];
1347
+ }).sort((a, b) => a.path.localeCompare(b.path));
1348
+ }
1349
+ function configuredPageIds(nodes) {
1350
+ let ids = /* @__PURE__ */ new Set();
1351
+ for (let node of nodes ?? []) {
1352
+ if (node.type === "page") {
1353
+ ids.add(node.file);
1354
+ continue;
1355
+ }
1356
+ if ("children" in node)
1357
+ for (let id of configuredPageIds(node.children))
1358
+ ids.add(id);
1359
+ }
1360
+ return ids;
1361
+ }
1362
+ function createManifest(config, content) {
1363
+ let routesByGroup = /* @__PURE__ */ new Map();
1364
+ for (let route of content.routes) {
1365
+ let groupRoutes = routesByGroup.get(route.group);
1366
+ groupRoutes || (groupRoutes = /* @__PURE__ */ new Map(), routesByGroup.set(route.group, groupRoutes)), groupRoutes.has(route.fileID) || groupRoutes.set(route.fileID, route);
1367
+ }
1368
+ let pages = [], pagesByGroup = /* @__PURE__ */ new Map(), pagesByFileIDByGroup = /* @__PURE__ */ new Map();
1369
+ for (let file of content.files) {
1370
+ let route = routesByGroup.get(file.group)?.get(file.fileID);
1371
+ if (!route) continue;
1372
+ let page = {
1373
+ fileID: file.fileID,
1374
+ group: file.group,
1375
+ slug: route.slug,
1376
+ path: route.path,
1377
+ title: file.title,
1378
+ description: file.description,
1379
+ og: file.og,
1380
+ status: file.status,
1381
+ publishedAt: file.publishedAt,
1382
+ updatedAt: file.updatedAt,
1383
+ authors: file.authors,
1384
+ tags: file.tags,
1385
+ meta: file.meta,
1386
+ body: file.body
1387
+ };
1388
+ pages.push(page);
1389
+ let groupPages = pagesByGroup.get(page.group);
1390
+ groupPages ? groupPages.push(page) : pagesByGroup.set(page.group, [page]);
1391
+ let pagesByFileID = pagesByFileIDByGroup.get(page.group);
1392
+ pagesByFileID || (pagesByFileID = /* @__PURE__ */ new Map(), pagesByFileIDByGroup.set(page.group, pagesByFileID)), pagesByFileID.has(page.fileID) || pagesByFileID.set(page.fileID, page);
1393
+ }
1394
+ let configuredPageIDsByGroup = /* @__PURE__ */ new Map();
1395
+ for (let group of Object.values(config.groups))
1396
+ configuredPageIDsByGroup.set(
1397
+ group.key,
1398
+ configuredPageIds(group.tree)
1399
+ );
1400
+ let treeCache = /* @__PURE__ */ new Map();
1401
+ function groupTree(groupKey, stack = /* @__PURE__ */ new Set()) {
1402
+ let cached = treeCache.get(groupKey);
1403
+ if (cached) return cached;
1404
+ if (stack.has(groupKey)) return [];
1405
+ let group = config.groups[groupKey];
1406
+ if (!group) return [];
1407
+ stack.add(groupKey);
1408
+ let groupPages = pagesByGroup.get(group.key) ?? [], pagesByFileID = pagesByFileIDByGroup.get(group.key) ?? /* @__PURE__ */ new Map(), routesByFileID = routesByGroup.get(group.key) ?? /* @__PURE__ */ new Map(), configTree = group.tree, configuredPageIDs = configuredPageIDsByGroup.get(group.key), tree2 = configTree ? [
1409
+ ...configTree.map(
1410
+ (node) => normalizeTreeNode(
1411
+ node,
1412
+ group.key,
1413
+ pagesByFileID,
1414
+ routesByFileID,
1415
+ {
1416
+ childTreeForGroup: (childGroup) => groupTree(childGroup, new Set(stack)),
1417
+ config
1418
+ }
1419
+ )
1420
+ ).filter((node) => node !== null),
1421
+ ...defaultTree(
1422
+ group.key,
1423
+ groupPages.filter((page) => !configuredPageIDs?.has(page.fileID)),
1424
+ routesByFileID
1425
+ )
1426
+ ] : defaultTree(group.key, groupPages, routesByFileID);
1427
+ return stack.delete(groupKey), treeCache.set(groupKey, tree2), tree2;
1428
+ }
1429
+ let groups = Object.fromEntries(
1430
+ Object.values(config.groups).map((group) => {
1431
+ let groupPages = pagesByGroup.get(group.key) ?? [], groupData = {
1432
+ key: group.key,
1433
+ title: group.title ?? group.key,
1434
+ path: group.path,
1435
+ origin: group.origin ?? (group.key === ROOT_CONTENT_GROUP_KEY ? void 0 : config.origin),
1436
+ ancestry: group.ancestry,
1437
+ ...group.og ? { og: group.og } : {},
1438
+ meta: group.meta,
1439
+ tree: groupTree(group.key),
1440
+ pages: groupPages
1441
+ };
1442
+ return [group.key, groupData];
1443
+ })
1444
+ ), tree = Object.values(groups).filter((group) => !config.groups[group.key]?.parentKey).flatMap(
1445
+ (group) => group.key === ROOT_CONTENT_GROUP_KEY ? group.tree : [
1446
+ {
1447
+ type: "section",
1448
+ id: group.key,
1449
+ title: group.title,
1450
+ path: group.path,
1451
+ group: group.key,
1452
+ children: group.tree
1453
+ }
1454
+ ]
1455
+ ), manifest = {
1456
+ site: content.site,
1457
+ groups,
1458
+ tree,
1459
+ routes: content.routes,
1460
+ pages,
1461
+ contentHash: ""
1462
+ }, { contentHash: _contentHash, ...hashInput } = manifest, canonical = stableStringify(hashInput), hash = createHash("sha256").update(canonical).digest("hex");
1463
+ return manifest.contentHash = hash.slice(0, 16), manifest;
1464
+ }
1465
+
1466
+ // src/lib/compiler/source/prepare-publish-os-source.ts
1467
+ async function preparePublishOsSource({
1468
+ publishOsDir,
1469
+ sourceIdentity
1470
+ }) {
1471
+ try {
1472
+ let sourceRoot = resolve5(publishOsDir), boundary = await assertPublishOsSourceBoundary(
1473
+ sourceRoot,
1474
+ sourceIdentity
1475
+ ), config = await resolvePublishOsConfig(sourceRoot), content = await scanContent(config), media = await buildMediaAssets({
1476
+ config,
1477
+ publishOsDir
1478
+ }), faviconsAndLogos = await buildFaviconsAndLogos({ publishOsDir }), missingPages = await missingPageReferences({
1479
+ sourceRoot,
1480
+ config,
1481
+ content
1482
+ });
1483
+ return {
1484
+ diagnostics: [
1485
+ ...boundary.nestedSources.map((file) => ({
1486
+ code: "SOURCE_NESTED_EXCLUDED",
1487
+ level: "warning",
1488
+ message: "Nested publish.os source is excluded from this site.",
1489
+ details: { file }
1490
+ })),
1491
+ ...content.warnings,
1492
+ ...missingPages,
1493
+ ...media.warnings.map((warning2) => ({
1494
+ ...warning2,
1495
+ level: "warning"
1496
+ })),
1497
+ ...faviconsAndLogos.warnings.map((warning2) => ({
1498
+ ...warning2,
1499
+ level: "warning"
1500
+ }))
1501
+ ],
1502
+ source: {
1503
+ assets: { faviconsAndLogos, media },
1504
+ config,
1505
+ content,
1506
+ manifest: createManifest(config, content)
1507
+ }
1508
+ };
1509
+ } catch (error) {
1510
+ return error instanceof AppError2 || error instanceof AppError ? { diagnostics: [appErrorToDiagnostic(error)] } : {
1511
+ diagnostics: [
1512
+ {
1513
+ code: "SOURCE_PREPARE_UNKNOWN",
1514
+ level: "error",
1515
+ message: String(error)
1516
+ }
1517
+ ]
1518
+ };
1519
+ }
1520
+ }
1521
+
1522
+ // src/lib/compiler/theme/theme-token-diagnostics.ts
1523
+ import { readdirSync, readFileSync } from "fs";
1524
+ import { extname as extname2, join as join3, resolve as resolve6 } from "path";
1525
+ import postcss from "postcss";
1526
+ function cssFiles(directory) {
1527
+ return readdirSync(directory, { withFileTypes: !0 }).flatMap((entry) => {
1528
+ let path = join3(directory, entry.name);
1529
+ return entry.isDirectory() ? cssFiles(path) : extname2(entry.name) === ".css" ? [path] : [];
1530
+ });
1531
+ }
1532
+ function readDesignTokens() {
1533
+ let { packageRoot } = readInstalledFrameworkLocation(), assets = resolve6(packageRoot, "compiler-assets/nextjs"), tokens = /* @__PURE__ */ new Set();
1534
+ for (let path of cssFiles(assets))
1535
+ postcss.parse(readFileSync(path, "utf-8"), { from: path }).walkDecls((declaration) => {
1536
+ declaration.prop.startsWith("--") && tokens.add(declaration.prop);
1537
+ });
1538
+ return Object.freeze([...tokens].sort());
1539
+ }
1540
+ var designTokens = readDesignTokens();
1541
+ function editDistance(left, right) {
1542
+ let previous = Array.from(
1543
+ { length: right.length + 1 },
1544
+ (_, index) => index
1545
+ );
1546
+ for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
1547
+ let current = [leftIndex];
1548
+ for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1)
1549
+ current[rightIndex] = Math.min(
1550
+ (current[rightIndex - 1] ?? 0) + 1,
1551
+ (previous[rightIndex] ?? 0) + 1,
1552
+ (previous[rightIndex - 1] ?? 0) + (left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1)
1553
+ );
1554
+ previous.splice(0, previous.length, ...current);
1555
+ }
1556
+ return previous[right.length] ?? right.length;
1557
+ }
1558
+ function nearestThemeDesignToken(token) {
1559
+ let nearest, distance = Number.POSITIVE_INFINITY;
1560
+ for (let candidate of designTokens) {
1561
+ let candidateDistance = editDistance(token, candidate);
1562
+ candidateDistance < distance && (nearest = candidate, distance = candidateDistance);
1563
+ }
1564
+ return distance <= 2 ? nearest : void 0;
1565
+ }
1566
+
1567
+ // src/lib/compiler/validation/validate-publish-os-source.ts
1568
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
1569
+ ".js",
1570
+ ".jsx",
1571
+ ".ts",
1572
+ ".tsx",
1573
+ ".mjs",
1574
+ ".cjs"
1575
+ ]), ATTRIBUTION_VALIDATION_EXTENSIONS = /* @__PURE__ */ new Set([
1576
+ ...SOURCE_EXTENSIONS,
1577
+ ".css"
1578
+ ]), MARKDOWN_ICON_SCAN_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx"]), FONT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".woff", ".woff2", ".otf", ".ttf"]), FORBIDDEN_ATTRIBUTION_REFERENCES = [
1579
+ "data-publishos-attribution",
1580
+ "publishos-attribution"
1581
+ ], ALLOWED_BARE_IMPORTS = new Set(supportedNextjsSourceImports()), BLOCKED_ICON_PACKAGE_IMPORTS = /* @__PURE__ */ new Set([
1582
+ "lucide-react",
1583
+ "@phosphor-icons/react",
1584
+ "@tabler/icons-react"
1585
+ ]), RUNTIME_TYPES_IMPORT = "@publish.os/nextjs/types", LEGACY_TYPES_IMPORT = "@publish.os/types", RUNTIME_TYPE_EXPORTS = /* @__PURE__ */ new Set([
1586
+ "IndexPageProps",
1587
+ "GroupContext",
1588
+ "LayoutProps",
1589
+ "ManifestLike",
1590
+ "NavFolder",
1591
+ "NavItem",
1592
+ "NavLink",
1593
+ "NavPage",
1594
+ "NavSection",
1595
+ "PageData",
1596
+ "PageProps",
1597
+ "RootLayoutProps",
1598
+ "RootPageProps",
1599
+ "SiteData",
1600
+ "TreeNode"
1601
+ ]), ROUTE_EXECUTION_EXPORTS = [
1602
+ "runtime",
1603
+ "dynamic",
1604
+ "revalidate",
1605
+ "preferredRegion",
1606
+ "maxDuration"
1607
+ ], LOCKFILES = /* @__PURE__ */ new Set([
1608
+ "pnpm-lock.yaml",
1609
+ "package-lock.json",
1610
+ "yarn.lock",
1611
+ "bun.lock",
1612
+ "bun.lockb"
1613
+ ]);
1614
+ function hasInternalSegment(path) {
1615
+ return path.split(/[\\/]/).includes("_internal");
1616
+ }
1617
+ async function fileExists(path) {
1618
+ return await stat3(path).then((entry) => entry.isFile()).catch(() => !1);
1619
+ }
1620
+ async function directoryExists(path) {
1621
+ return await stat3(path).then((entry) => entry.isDirectory()).catch(() => !1);
1622
+ }
1623
+ async function walkAllFiles(dir, prefix = "") {
1624
+ let entries = await readdir3(dir, { withFileTypes: !0 }).catch(() => []), files = [];
1625
+ for (let entry of entries) {
1626
+ let relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
1627
+ if (isExcludedSourcePath(relativePath)) continue;
1628
+ let absolutePath = resolve7(dir, entry.name);
1629
+ if (entry.isDirectory()) {
1630
+ files.push(...await walkAllFiles(absolutePath, relativePath));
1631
+ continue;
1632
+ }
1633
+ files.push(relativePath);
1634
+ }
1635
+ return files;
1636
+ }
1637
+ function normalizeRelativePath(path) {
1638
+ return path.split(sep).join("/");
1639
+ }
1640
+ function isRecord(value) {
1641
+ return !!(value && typeof value == "object" && !Array.isArray(value));
1642
+ }
1643
+ function isLocalFontSourcePath(value) {
1644
+ return !/^[a-z][a-z0-9+.-]*:/i.test(value) && !value.startsWith("//");
1645
+ }
1646
+ function normalizeLocalFontSourcePath(value) {
1647
+ return value.replace(/^\/+/, "").replace(/^\.\//, "");
1648
+ }
1649
+ function isFontFilePath(path) {
1650
+ return FONT_FILE_EXTENSIONS.has(extname3(path).toLowerCase());
1651
+ }
1652
+ function isValidSourceFontFilePath(path) {
1653
+ return path.startsWith("fonts/") && isFontFilePath(path);
1654
+ }
1655
+ function fontConfigHint(path) {
1656
+ return `To use this font, place it under publish.os/fonts and declare it in publish.os.config.json, for example: "fonts": { "main": { "family": "Your Font", "src": "/${path}", "weight": "400" } }.`;
1657
+ }
1658
+ function collectFontRoleSourcePaths(roleConfig, sourcePaths) {
1659
+ if (!roleConfig || typeof roleConfig == "string" || !isRecord(roleConfig))
1660
+ return;
1661
+ let addSource = (value) => {
1662
+ typeof value != "string" || !isLocalFontSourcePath(value) || sourcePaths.add(normalizeLocalFontSourcePath(value));
1663
+ }, addFile = (value) => {
1664
+ if (!isRecord(value)) return;
1665
+ let path = value.path;
1666
+ typeof path == "string" && sourcePaths.add(normalizeLocalFontSourcePath(path));
1667
+ };
1668
+ if (addSource(roleConfig.src), Array.isArray(roleConfig.src))
1669
+ for (let file of roleConfig.src) addFile(file);
1670
+ }
1671
+ async function validateConfiguredLocalFontSources({
1672
+ config,
1673
+ publishOsDir
1674
+ }) {
1675
+ let sourcePaths = /* @__PURE__ */ new Set(), collect = async (fonts, sourceFile) => {
1676
+ let paths = /* @__PURE__ */ new Set();
1677
+ for (let font of Object.values(fonts ?? {}))
1678
+ collectFontRoleSourcePaths(font, paths);
1679
+ for (let path of paths)
1680
+ await assertSourceDependency({
1681
+ publishOsDir,
1682
+ path: resolve7(publishOsDir, path),
1683
+ sourceFile
1684
+ }), sourcePaths.add(path);
1685
+ };
1686
+ await collect(
1687
+ config.fonts,
1688
+ "publish.os.config.json"
1689
+ );
1690
+ for (let group of Object.values(config.groups))
1691
+ await collect(group.fonts, group.__meta.filePath);
1692
+ return sourcePaths;
1693
+ }
1694
+ function isHardRejectedPath(path) {
1695
+ let name = basename(path);
1696
+ return isReservedFrameworkPath(path) || path.split("/").some((segment) => ["packages", "scripts"].includes(segment)) || LOCKFILES.has(name) || name === "tsconfig.json" && path !== "tsconfig.json";
1697
+ }
1698
+ function isAllowedBareImport(specifier) {
1699
+ if (specifier.startsWith("@components/")) {
1700
+ let componentPath = specifier.slice(12), segments = componentPath.split("/");
1701
+ return componentPath.length > 0 && !componentPath.includes("\\") && !componentPath.includes("\0") && segments.every(
1702
+ (segment) => segment !== "" && segment !== "." && segment !== ".."
1703
+ );
1704
+ }
1705
+ return ALLOWED_BARE_IMPORTS.has(specifier);
1706
+ }
1707
+ function addFileError(store, code, message, file, details = {}) {
1708
+ store.error(code, message, { ...details, file });
1709
+ }
1710
+ function addFileWarning(store, code, message, file, details = {}) {
1711
+ store.warning(code, message, { ...details, file });
1712
+ }
1713
+ function validateThemeTokenSpelling(css, file, store) {
1714
+ let seen = /* @__PURE__ */ new Set();
1715
+ for (let match of css.matchAll(/--[a-z0-9-]+/gi)) {
1716
+ let token = match[0].toLowerCase();
1717
+ if (seen.has(token)) continue;
1718
+ seen.add(token);
1719
+ let correction = nearestThemeDesignToken(token);
1720
+ !correction || correction === token || addFileWarning(
1721
+ store,
1722
+ "FRAMEWORK_THEME_TOKEN_SPELLING",
1723
+ `Unknown design token "${token}". Did you mean "${correction}"?`,
1724
+ file,
1725
+ {
1726
+ correction,
1727
+ ...sourceDetails(css, match.index ?? 0, match[0])
1728
+ }
1729
+ );
1730
+ }
1731
+ }
1732
+ function getSourceLocation(source, index) {
1733
+ let before = source.slice(0, index), line = before.split(`
1734
+ `).length, previousLineBreak = before.lastIndexOf(`
1735
+ `), nextLineBreak = source.indexOf(`
1736
+ `, index), lineStart = previousLineBreak + 1, lineEnd = nextLineBreak === -1 ? source.length : nextLineBreak;
1737
+ return {
1738
+ line,
1739
+ column: index - previousLineBreak,
1740
+ sourceLine: source.slice(lineStart, lineEnd).trimEnd()
1741
+ };
1742
+ }
1743
+ function sourceDetails(source, index, snippet) {
1744
+ return {
1745
+ ...getSourceLocation(source, index),
1746
+ source: snippet.trim()
1747
+ };
1748
+ }
1749
+ function validateAttributionReferences(source, file, store) {
1750
+ for (let reference of FORBIDDEN_ATTRIBUTION_REFERENCES) {
1751
+ let index = source.indexOf(reference);
1752
+ index !== -1 && addFileError(
1753
+ store,
1754
+ "FRAMEWORK_ATTRIBUTION_SELECTOR_RESERVED",
1755
+ "Source code cannot target or reference the platform powered-by attribution selectors.",
1756
+ file,
1757
+ {
1758
+ reference,
1759
+ ...sourceDetails(source, index, reference)
1760
+ }
1761
+ );
1762
+ }
1763
+ }
1764
+ function parseNamedImports(statement) {
1765
+ let imports = /\b(?:import|export)\s+(?:type\s+)?\{([\s\S]*?)\}\s+from\b/.exec(statement)?.[1];
1766
+ return imports ? imports.split(",").map((name) => name.trim().replace(/^type\s+/, "")).filter(Boolean) : [];
1767
+ }
1768
+ function runtimeTypeName(importedName) {
1769
+ return importedName.split(/\s+as\s+/i)[0]?.trim() ?? importedName;
1770
+ }
1771
+ function importSuggestion(options) {
1772
+ if (options.specifier !== LEGACY_TYPES_IMPORT) return;
1773
+ let imports = options.statement ? parseNamedImports(options.statement).filter(
1774
+ (name) => RUNTIME_TYPE_EXPORTS.has(runtimeTypeName(name))
1775
+ ) : [];
1776
+ return imports.length > 0 ? `Use: import type { ${imports.join(", ")} } from "${RUNTIME_TYPES_IMPORT}";` : `PublishOS page props and runtime types are exported from "${RUNTIME_TYPES_IMPORT}", not "${LEGACY_TYPES_IMPORT}".`;
1777
+ }
1778
+ function importDetails(options) {
1779
+ let suggestion = importSuggestion({
1780
+ specifier: options.specifier,
1781
+ statement: options.statement
1782
+ }), source = options.statement?.includes(`
1783
+ `) ? options.statement.trim() : options.location?.sourceLine.trim() ?? options.statement?.trim();
1784
+ return {
1785
+ specifier: options.specifier,
1786
+ ...options.location ?? {},
1787
+ ...source ? { source } : {},
1788
+ ...suggestion ? { suggestion } : {},
1789
+ ...options.extras ?? {}
1790
+ };
1791
+ }
1792
+ function scriptKindForPath(path) {
1793
+ switch (extname3(path).toLowerCase()) {
1794
+ case ".tsx":
1795
+ return ts2.ScriptKind.TSX;
1796
+ case ".jsx":
1797
+ return ts2.ScriptKind.JSX;
1798
+ case ".ts":
1799
+ return ts2.ScriptKind.TS;
1800
+ default:
1801
+ return ts2.ScriptKind.JS;
1802
+ }
1803
+ }
1804
+ function hasExportModifier(node) {
1805
+ return !!(ts2.canHaveModifiers(node) && ts2.getModifiers(node)?.some((modifier) => modifier.kind === ts2.SyntaxKind.ExportKeyword));
1806
+ }
1807
+ function isRouteOverride(file, groupFolders) {
1808
+ if (file.startsWith("components/")) return !1;
1809
+ for (let folder of ["", ...groupFolders]) {
1810
+ let prefix = folder ? `${folder}/` : "";
1811
+ if (file.startsWith(prefix) && /^(?:layout|index|page)\.tsx$|^\(index\)\/(?:layout|index)\.tsx$|^\(page\)\/(?:layout|page)\.tsx$/.test(
1812
+ file.slice(prefix.length)
1813
+ ))
1814
+ return !0;
1815
+ }
1816
+ return !1;
1817
+ }
1818
+ function hasDefaultExport(sourceFile) {
1819
+ return sourceFile.statements.some((statement) => ts2.isExportAssignment(statement) ? !statement.isExportEquals : ts2.isFunctionDeclaration(statement) || ts2.isClassDeclaration(statement) ? !!statement.modifiers?.some(
1820
+ (modifier) => modifier.kind === ts2.SyntaxKind.DefaultKeyword
1821
+ ) : ts2.isExportDeclaration(statement) && !statement.isTypeOnly && statement.exportClause && ts2.isNamedExports(statement.exportClause) ? statement.exportClause.elements.some(
1822
+ (element) => !element.isTypeOnly && element.name.text === "default"
1823
+ ) : !1);
1824
+ }
1825
+ function invalidMdxComponentValue(sourceFile, name) {
1826
+ for (let statement of sourceFile.statements)
1827
+ if (!(!ts2.isVariableStatement(statement) || !hasExportModifier(statement)))
1828
+ for (let declaration of statement.declarationList.declarations) {
1829
+ if (!ts2.isIdentifier(declaration.name) || declaration.name.text !== name)
1830
+ continue;
1831
+ let value = declaration.initializer;
1832
+ if (value && !ts2.isArrowFunction(value) && !ts2.isFunctionExpression(value) && !ts2.isCallExpression(value) && !ts2.isIdentifier(value) && !ts2.isPropertyAccessExpression(value) && !ts2.isParenthesizedExpression(value))
1833
+ return value;
1834
+ }
1835
+ }
1836
+ function addRouteExecutionExportError(options) {
1837
+ let start = options.node.getStart(options.sourceFile);
1838
+ addFileError(
1839
+ options.store,
1840
+ "FRAMEWORK_ROUTE_EXECUTION_EXPORT",
1841
+ `Source modules cannot export Next.js route execution setting '${options.exportName}'.`,
1842
+ options.relativeFile,
1843
+ {
1844
+ exportName: options.exportName,
1845
+ ...sourceDetails(
1846
+ options.source,
1847
+ start,
1848
+ options.node.getText(options.sourceFile)
1849
+ )
1850
+ }
1851
+ );
1852
+ }
1853
+ function commonJsExportName(node) {
1854
+ if (node.operatorToken.kind !== ts2.SyntaxKind.EqualsToken) return;
1855
+ let left = node.left;
1856
+ if (ts2.isPropertyAccessExpression(left) && (ts2.isIdentifier(left.expression) && left.expression.text === "exports" || ts2.isPropertyAccessExpression(left.expression) && ts2.isIdentifier(left.expression.expression) && left.expression.expression.text === "module" && left.expression.name.text === "exports"))
1857
+ return left.name.text;
1858
+ }
1859
+ async function validateSourceModule(options) {
1860
+ let sourceFile = ts2.createSourceFile(
1861
+ options.relativeFile,
1862
+ options.source,
1863
+ ts2.ScriptTarget.Latest,
1864
+ !0,
1865
+ scriptKindForPath(options.relativeFile)
1866
+ ), parseDiagnostics = sourceFile.parseDiagnostics;
1867
+ for (let diagnostic of parseDiagnostics ?? []) {
1868
+ let start = diagnostic.start ?? 0;
1869
+ addFileError(
1870
+ options.store,
1871
+ "FRAMEWORK_SOURCE_PARSE_FAILED",
1872
+ `Source module could not be parsed: ${ts2.flattenDiagnosticMessageText(diagnostic.messageText, " ")}`,
1873
+ options.relativeFile,
1874
+ sourceDetails(options.source, start, "")
1875
+ );
1876
+ }
1877
+ !parseDiagnostics?.length && isRouteOverride(options.relativeFile, options.groupFolders) && !hasDefaultExport(sourceFile) && addFileError(
1878
+ options.store,
1879
+ "SOURCE_OVERRIDE_DEFAULT_EXPORT_MISSING",
1880
+ "Route override files must have a default export. Export the component as default, or remove the file to use the framework default.",
1881
+ options.relativeFile
1882
+ );
1883
+ let mdxComponent = /^components\/mdx\/([a-z0-9]+(?:-[a-z0-9]+)*)\.tsx$/.exec(
1884
+ options.relativeFile
1885
+ );
1886
+ if (mdxComponent && !parseDiagnostics?.length) {
1887
+ let stem = mdxComponent[1] ?? "", name = mdxComponentExportName(stem), exports = namedValueExports(sourceFile);
1888
+ if (name && !exports.has(name))
1889
+ addFileError(
1890
+ options.store,
1891
+ "FRAMEWORK_MDX_COMPONENT_EXPORT_MISSING",
1892
+ `${options.relativeFile} must export a named ${name} component.`,
1893
+ options.relativeFile,
1894
+ sourceDetails(options.source, 0, `export { ${name} }`)
1895
+ );
1896
+ else if (name) {
1897
+ let optionalExports = stem === "figure" ? Object.keys(groupedMdxExports.figure) : stem === "table" ? Object.keys(groupedMdxExports.table) : [];
1898
+ for (let exportName of [name, ...optionalExports]) {
1899
+ if (!exports.has(exportName)) continue;
1900
+ let invalidValue = invalidMdxComponentValue(sourceFile, exportName);
1901
+ invalidValue && addFileError(
1902
+ options.store,
1903
+ "FRAMEWORK_MDX_COMPONENT_EXPORT_INVALID",
1904
+ `${options.relativeFile} must export ${exportName} as a React component, not a static value.`,
1905
+ options.relativeFile,
1906
+ sourceDetails(
1907
+ options.source,
1908
+ invalidValue.getStart(sourceFile),
1909
+ `export function ${exportName}`
1910
+ )
1911
+ );
1912
+ }
1913
+ }
1914
+ }
1915
+ let imports = [], visit = (node) => {
1916
+ if (ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node))
1917
+ node.moduleSpecifier && imports.push({
1918
+ kind: ts2.isImportDeclaration(node) ? "import" : "export",
1919
+ node,
1920
+ specifier: ts2.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : void 0
1921
+ });
1922
+ else if (ts2.isImportEqualsDeclaration(node) && ts2.isExternalModuleReference(node.moduleReference)) {
1923
+ let expression = node.moduleReference.expression;
1924
+ imports.push({
1925
+ kind: "import",
1926
+ node,
1927
+ specifier: expression && ts2.isStringLiteral(expression) ? expression.text : void 0
1928
+ });
1929
+ }
1930
+ if (ts2.isVariableStatement(node) && hasExportModifier(node))
1931
+ for (let declaration of node.declarationList.declarations)
1932
+ for (let exportName of collectBindingNames(declaration.name))
1933
+ ROUTE_EXECUTION_EXPORTS.includes(exportName) && addRouteExecutionExportError({
1934
+ exportName,
1935
+ node,
1936
+ relativeFile: options.relativeFile,
1937
+ source: options.source,
1938
+ sourceFile,
1939
+ store: options.store
1940
+ });
1941
+ if (ts2.isExportDeclaration(node) && node.exportClause && ts2.isNamedExports(node.exportClause))
1942
+ for (let element of node.exportClause.elements) {
1943
+ let exportName = element.name.text;
1944
+ ROUTE_EXECUTION_EXPORTS.includes(exportName) && addRouteExecutionExportError({
1945
+ exportName,
1946
+ node: element,
1947
+ relativeFile: options.relativeFile,
1948
+ source: options.source,
1949
+ sourceFile,
1950
+ store: options.store
1951
+ });
1952
+ }
1953
+ if (ts2.isBinaryExpression(node)) {
1954
+ let exportName = commonJsExportName(node);
1955
+ exportName && ROUTE_EXECUTION_EXPORTS.includes(exportName) && addRouteExecutionExportError({
1956
+ exportName,
1957
+ node,
1958
+ relativeFile: options.relativeFile,
1959
+ source: options.source,
1960
+ sourceFile,
1961
+ store: options.store
1962
+ });
1963
+ }
1964
+ if (ts2.isCallExpression(node)) {
1965
+ let kind = node.expression.kind === ts2.SyntaxKind.ImportKeyword ? "dynamic import" : ts2.isIdentifier(node.expression) && node.expression.text === "require" ? "require" : void 0;
1966
+ if (kind) {
1967
+ let argument = node.arguments[0];
1968
+ imports.push({
1969
+ kind,
1970
+ node,
1971
+ specifier: node.arguments.length === 1 && argument && ts2.isStringLiteral(argument) ? argument.text : void 0
1972
+ });
1973
+ }
1974
+ }
1975
+ ts2.forEachChild(node, visit);
1976
+ };
1977
+ visit(sourceFile);
1978
+ for (let moduleImport of imports) {
1979
+ let start = moduleImport.node.getStart(sourceFile), statement = moduleImport.node.getText(sourceFile), location = getSourceLocation(options.source, start);
1980
+ if (moduleImport.specifier === void 0) {
1981
+ addFileError(
1982
+ options.store,
1983
+ "FRAMEWORK_NON_LITERAL_IMPORT",
1984
+ `Non-literal ${moduleImport.kind} specifiers are not allowed in source modules.`,
1985
+ options.relativeFile,
1986
+ {
1987
+ ...location,
1988
+ source: statement.trim()
1989
+ }
1990
+ );
1991
+ continue;
1992
+ }
1993
+ await validateImportSpecifier({
1994
+ specifier: moduleImport.specifier,
1995
+ publishOsDir: options.publishOsDir,
1996
+ sourceFile: options.absolutePath,
1997
+ relativeFile: options.relativeFile,
1998
+ store: options.store,
1999
+ location,
2000
+ statement,
2001
+ groupFolders: options.groupFolders
2002
+ });
2003
+ }
2004
+ }
2005
+ function validateMdxComponentPath(file, store) {
2006
+ if (!file.startsWith("components/mdx/")) return !0;
2007
+ let stem = /^components\/mdx\/([a-z0-9]+(?:-[a-z0-9]+)*)\.tsx$/.exec(
2008
+ file
2009
+ )?.[1];
2010
+ if (!stem)
2011
+ return addFileError(
2012
+ store,
2013
+ "FRAMEWORK_MDX_COMPONENT_PATH_INVALID",
2014
+ "MDX components must be one kebab-case .tsx file directly under components/mdx/.",
2015
+ file
2016
+ ), !1;
2017
+ if (!Object.hasOwn(replacedMdxSourceFiles, stem)) return !0;
2018
+ let replacement = replacedMdxSourceFiles[stem];
2019
+ return addFileError(
2020
+ store,
2021
+ "FRAMEWORK_MDX_COMPONENT_FILE_REPLACED",
2022
+ `Use components/mdx/${replacement}.tsx instead of ${file}. Related MDX parts belong in the same source file.`,
2023
+ file,
2024
+ { suggestion: `components/mdx/${replacement}.tsx` }
2025
+ ), !1;
2026
+ }
2027
+ function validateSourceFilePath({
2028
+ file,
2029
+ groupFolders,
2030
+ store
2031
+ }) {
2032
+ if (basename(file) === "leaf.tsx" && (file === "leaf.tsx" || groupFolders.has(dirname(file))) || file.split("/").includes("(leaf)")) {
2033
+ let suggestedPath = file.replaceAll("(leaf)", "(page)").replace(/leaf\.tsx$/, "page.tsx");
2034
+ return addFileError(
2035
+ store,
2036
+ "FRAMEWORK_LEGACY_PAGE_PATH",
2037
+ `Rename ${file} to ${suggestedPath} for alpha.9. Rename index renderers to index.tsx and their branch to (index) in the same migration.`,
2038
+ file,
2039
+ { suggestion: suggestedPath }
2040
+ ), !1;
2041
+ }
2042
+ return validateMdxComponentPath(file, store) ? isHardRejectedPath(file) ? (addFileError(
2043
+ store,
2044
+ "FRAMEWORK_SOURCE_PATH_REJECTED",
2045
+ "This file path is not allowed inside publish.os.",
2046
+ file
2047
+ ), !1) : !0 : !1;
2048
+ }
2049
+ function validateSourceFontFile({
2050
+ file,
2051
+ configuredFontSourcePaths,
2052
+ store
2053
+ }) {
2054
+ if (!isFontFilePath(file)) return !1;
2055
+ let configured = configuredFontSourcePaths.has(file);
2056
+ return isValidSourceFontFilePath(file) ? (configured || addFileWarning(
2057
+ store,
2058
+ "FRAMEWORK_FONT_FILE_UNUSED",
2059
+ `Font file "${basename(file)}" is not used by the current font configuration and will not be included in the site.`,
2060
+ file,
2061
+ { fontSourcePath: `/${file}`, suggestion: fontConfigHint(file) }
2062
+ ), !0) : (addFileWarning(
2063
+ store,
2064
+ configured ? "FRAMEWORK_FONT_FILE_PATH_UNSUPPORTED" : "FRAMEWORK_FONT_FILE_PATH_IGNORED",
2065
+ configured ? "This local font is referenced from font config, but local font files should live under publish.os/fonts." : "Font files must live under publish.os/fonts and be used by a fonts entry. This file will not be included in the site.",
2066
+ file,
2067
+ {
2068
+ fontSourcePath: `/${file}`,
2069
+ suggestion: fontConfigHint(`fonts/${basename(file)}`)
2070
+ }
2071
+ ), !0);
2072
+ }
2073
+ function validateLegacyIconReferences(source, file, store) {
2074
+ let match = findLegacyIconResolver(source);
2075
+ match && addFileError(
2076
+ store,
2077
+ "FRAMEWORK_ICON_RESOLVER_REJECTED",
2078
+ 'The Icon resolver is removed. Import an exact component from "@phosphor-icons/react/ssr" in a custom MDX component instead.',
2079
+ file,
2080
+ sourceDetails(source, match.index, match[0] ?? "")
2081
+ );
2082
+ }
2083
+ function validateSourceModuleSafety(source, file, store) {
2084
+ hasInternalSegment(file) && addFileError(
2085
+ store,
2086
+ "FRAMEWORK_INTERNAL_SOURCE_RESERVED",
2087
+ "The _internal source namespace is reserved for generated platform files.",
2088
+ file
2089
+ );
2090
+ let dangerousHtmlMatch = /\bdangerouslySetInnerHTML\b/.exec(source);
2091
+ dangerousHtmlMatch && addFileWarning(
2092
+ store,
2093
+ "FRAMEWORK_DANGEROUS_HTML",
2094
+ "dangerouslySetInnerHTML requires manual review.",
2095
+ file,
2096
+ sourceDetails(
2097
+ source,
2098
+ dangerousHtmlMatch.index,
2099
+ dangerousHtmlMatch[0] ?? ""
2100
+ )
2101
+ );
2102
+ }
2103
+ async function validateSourceFileContents({
2104
+ publishOsDir,
2105
+ file,
2106
+ groupFolders,
2107
+ checkedCss,
2108
+ store
2109
+ }) {
2110
+ let extension = extname3(file), sourceModule = SOURCE_EXTENSIONS.has(extension), validatesAttribution = ATTRIBUTION_VALIDATION_EXTENSIONS.has(extension), scansLegacyIcons = sourceModule || MARKDOWN_ICON_SCAN_EXTENSIONS.has(extension) && !file.startsWith("assets/");
2111
+ if (!validatesAttribution && !scansLegacyIcons) return;
2112
+ let absolutePath = resolve7(publishOsDir, file), source = await readFile5(absolutePath, "utf-8");
2113
+ validatesAttribution && validateAttributionReferences(source, file, store), extension === ".css" && (validateThemeTokenSpelling(source, file, store), await validateCssDependencies({
2114
+ publishOsDir,
2115
+ filePath: absolutePath,
2116
+ css: source,
2117
+ checked: checkedCss
2118
+ })), scansLegacyIcons && validateLegacyIconReferences(source, file, store), sourceModule && (validateSourceModuleSafety(source, file, store), await validateSourceModule({
2119
+ absolutePath,
2120
+ groupFolders,
2121
+ publishOsDir,
2122
+ relativeFile: file,
2123
+ source,
2124
+ store
2125
+ }));
2126
+ }
2127
+ async function validateSourceBoundary(publishOsDir, config, store) {
2128
+ let checkedCss = /* @__PURE__ */ new Set(), configuredFontSourcePaths = await validateConfiguredLocalFontSources({
2129
+ config,
2130
+ publishOsDir
2131
+ }), groupFolders = new Set(
2132
+ Object.values(config.groups).map(
2133
+ (group) => group.__meta.relativeFolderPath
2134
+ )
2135
+ );
2136
+ await fileExists(resolve7(publishOsDir, "package.json")) && addFileError(
2137
+ store,
2138
+ "FRAMEWORK_SOURCE_PACKAGE_UNSUPPORTED",
2139
+ "publish.os/package.json is not supported; generated dependencies are controlled by the platform.",
2140
+ "package.json"
2141
+ );
2142
+ for (let file of await walkAllFiles(publishOsDir))
2143
+ validateSourceFilePath({ file, groupFolders, store }) && (validateSourceFontFile({ file, configuredFontSourcePaths, store }) || (isApprovedPublishOsPath(file, groupFolders) || addFileWarning(
2144
+ store,
2145
+ "FRAMEWORK_SOURCE_PATH_IGNORED",
2146
+ "This file is outside the supported publish.os structure and will not be included in the site.",
2147
+ file
2148
+ ), await validateSourceFileContents({
2149
+ publishOsDir,
2150
+ file,
2151
+ groupFolders,
2152
+ checkedCss,
2153
+ store
2154
+ })));
2155
+ }
2156
+ async function validateCssDependencies({
2157
+ publishOsDir,
2158
+ filePath,
2159
+ css,
2160
+ checked
2161
+ }) {
2162
+ if (!checked.has(filePath)) {
2163
+ checked.add(filePath);
2164
+ for (let imported of localCssImports({ css })) {
2165
+ let target = resolve7(dirname(filePath), imported.path);
2166
+ await assertSourceDependency({
2167
+ publishOsDir,
2168
+ path: target,
2169
+ sourceFile: filePath
2170
+ }), !checked.has(target) && await validateCssDependencies({
2171
+ publishOsDir,
2172
+ filePath: target,
2173
+ css: await readFile5(target, "utf8"),
2174
+ checked
2175
+ });
2176
+ }
2177
+ }
2178
+ }
2179
+ async function resolveRelativeImportTarget(sourceFile, specifier) {
2180
+ let target = resolve7(dirname(sourceFile), specifier);
2181
+ if (await fileExists(target)) return target;
2182
+ for (let extension of SOURCE_EXTENSIONS)
2183
+ if (await fileExists(`${target}${extension}`))
2184
+ return `${target}${extension}`;
2185
+ if (await directoryExists(target))
2186
+ for (let extension of SOURCE_EXTENSIONS) {
2187
+ let indexPath = resolve7(target, `index${extension}`);
2188
+ if (await fileExists(indexPath))
2189
+ return indexPath;
2190
+ }
2191
+ return null;
2192
+ }
2193
+ async function validateImportSpecifier(options) {
2194
+ let localTarget = options.specifier.startsWith(".") ? normalizeRelativePath(
2195
+ relative4(
2196
+ options.publishOsDir,
2197
+ resolve7(dirname(options.sourceFile), options.specifier)
2198
+ )
2199
+ ) : options.specifier.startsWith("@components/") ? `components/${options.specifier.slice(12)}` : void 0;
2200
+ if (localTarget && isExcludedSourcePath(localTarget)) {
2201
+ addFileError(
2202
+ options.store,
2203
+ "SOURCE_IMPORT_EXCLUDED",
2204
+ "Source modules cannot import nested sites, generated output, or IDE metadata.",
2205
+ options.relativeFile,
2206
+ importDetails(options)
2207
+ );
2208
+ return;
2209
+ }
2210
+ if (options.specifier.startsWith(".")) {
2211
+ let target = resolve7(dirname(options.sourceFile), options.specifier), relativeTarget = relative4(options.publishOsDir, target);
2212
+ if (hasInternalSegment(relativeTarget) && addFileError(
2213
+ options.store,
2214
+ "FRAMEWORK_INTERNAL_IMPORT_RESERVED",
2215
+ "Source modules cannot import the generated _internal source namespace.",
2216
+ options.relativeFile,
2217
+ importDetails(options)
2218
+ ), !isSameOrDescendant(options.publishOsDir, target)) {
2219
+ addFileError(
2220
+ options.store,
2221
+ "FRAMEWORK_RELATIVE_IMPORT_ESCAPE",
2222
+ "Relative imports cannot escape publish.os.",
2223
+ options.relativeFile,
2224
+ importDetails(options)
2225
+ );
2226
+ return;
2227
+ }
2228
+ let resolvedTarget = await resolveRelativeImportTarget(
2229
+ options.sourceFile,
2230
+ options.specifier
2231
+ );
2232
+ if (!resolvedTarget) {
2233
+ addFileError(
2234
+ options.store,
2235
+ "FRAMEWORK_RELATIVE_IMPORT_NOT_FOUND",
2236
+ "Relative imports must resolve to files inside the approved publish.os surface.",
2237
+ options.relativeFile,
2238
+ importDetails(options)
2239
+ );
2240
+ return;
2241
+ }
2242
+ let resolvedRelativeTarget = normalizeRelativePath(
2243
+ relative4(options.publishOsDir, resolvedTarget)
2244
+ );
2245
+ (isHardRejectedPath(resolvedRelativeTarget) || !isApprovedPublishOsPath(resolvedRelativeTarget, options.groupFolders)) && addFileError(
2246
+ options.store,
2247
+ "FRAMEWORK_RELATIVE_IMPORT_NOT_INCLUDED",
2248
+ "Relative imports must resolve to files in the supported publish.os structure.",
2249
+ options.relativeFile,
2250
+ importDetails({
2251
+ specifier: options.specifier,
2252
+ location: options.location,
2253
+ statement: options.statement,
2254
+ extras: { target: resolvedRelativeTarget }
2255
+ })
2256
+ );
2257
+ return;
2258
+ }
2259
+ if (options.specifier.includes("_internal")) {
2260
+ addFileError(
2261
+ options.store,
2262
+ "FRAMEWORK_INTERNAL_IMPORT_RESERVED",
2263
+ "Source modules cannot import the generated _internal source namespace.",
2264
+ options.relativeFile,
2265
+ importDetails(options)
2266
+ );
2267
+ return;
2268
+ }
2269
+ if (options.specifier === "@publish.os/nextjs/mdx/mdx") {
2270
+ addFileError(
2271
+ options.store,
2272
+ "FRAMEWORK_MDX_IMPORT_REJECTED",
2273
+ 'Import MDX from "@publish.os/nextjs/mdx" so the generated site component map is applied.',
2274
+ options.relativeFile,
2275
+ importDetails(options)
2276
+ );
2277
+ return;
2278
+ }
2279
+ if (options.specifier === "@publish.os/nextjs/icons" || options.specifier === "@publish.os/nextjs/kernel/framework-icons") {
2280
+ addFileError(
2281
+ options.store,
2282
+ "FRAMEWORK_ICON_ALIAS_REMOVED",
2283
+ `The icon alias "${options.specifier}" was removed. Import exact icons from "@phosphor-icons/react/ssr" instead.`,
2284
+ options.relativeFile,
2285
+ importDetails(options)
2286
+ );
2287
+ return;
2288
+ }
2289
+ if (BLOCKED_ICON_PACKAGE_IMPORTS.has(options.specifier)) {
2290
+ addFileError(
2291
+ options.store,
2292
+ "FRAMEWORK_ICON_PACKAGE_IMPORT_REJECTED",
2293
+ `Can't import from "${options.specifier}". Import exact component names from "@phosphor-icons/react/ssr" instead.`,
2294
+ options.relativeFile,
2295
+ importDetails(options)
2296
+ );
2297
+ return;
2298
+ }
2299
+ isAllowedBareImport(options.specifier) || addFileError(
2300
+ options.store,
2301
+ "FRAMEWORK_IMPORT_NOT_ALLOWED",
2302
+ `Can't import from "${options.specifier}". Source modules can only import approved runtime packages.`,
2303
+ options.relativeFile,
2304
+ importDetails(options)
2305
+ );
2306
+ }
2307
+ function toValidationDiagnostic(diagnostic) {
2308
+ return {
2309
+ ...diagnostic,
2310
+ file: typeof diagnostic.details?.file == "string" ? diagnostic.details.file : diagnostic.file,
2311
+ line: typeof diagnostic.details?.line == "number" ? diagnostic.details.line : diagnostic.line,
2312
+ column: typeof diagnostic.details?.column == "number" ? diagnostic.details.column : diagnostic.column
2313
+ };
2314
+ }
2315
+ async function validatePublishOsSource(options) {
2316
+ if (!options.publishOsDir)
2317
+ throw new AppError({
2318
+ code: "FRAMEWORK_VALIDATE_PATH_MISSING",
2319
+ message: "publishOsDir is required"
2320
+ });
2321
+ let publishOsDir = resolve7(options.publishOsDir), store = new DiagnosticStore(), source;
2322
+ try {
2323
+ let prepared = options.sourcePreparation ?? await preparePublishOsSource({
2324
+ publishOsDir,
2325
+ sourceIdentity: options.sourceIdentity
2326
+ });
2327
+ for (let diagnostic of prepared.diagnostics)
2328
+ store.add(diagnostic);
2329
+ prepared.source && (source = prepared.source, await validateSourceBoundary(publishOsDir, prepared.source.config, store));
2330
+ } catch (error) {
2331
+ error instanceof AppError ? store.error(error.code, error.message, error.details) : store.error("FRAMEWORK_VALIDATE_UNKNOWN", String(error));
2332
+ }
2333
+ let allDiagnostics = store.all().map(toValidationDiagnostic), errors = allDiagnostics.filter((d) => d.level === "error"), warnings = allDiagnostics.filter((d) => d.level === "warning"), valid = errors.length === 0;
2334
+ return {
2335
+ valid,
2336
+ errors,
2337
+ warnings,
2338
+ ...valid && source ? { source } : {}
2339
+ };
2340
+ }
2341
+
2342
+ export {
2343
+ validatePublishOsSource
2344
+ };