blume 1.6.4 → 1.6.6

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 (178) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/bin/blume.mjs +3 -2
  3. package/dist/cli/chunk-0ewz4trd.js +679 -0
  4. package/dist/cli/chunk-0ewz4trd.js.map +15 -0
  5. package/dist/cli/chunk-27gtm2ym.js +69 -0
  6. package/dist/cli/chunk-27gtm2ym.js.map +11 -0
  7. package/dist/cli/chunk-2aj8ddew.js +72 -0
  8. package/dist/cli/chunk-2aj8ddew.js.map +10 -0
  9. package/dist/cli/chunk-3k0kzs6d.js +69 -0
  10. package/dist/cli/chunk-3k0kzs6d.js.map +11 -0
  11. package/dist/cli/chunk-3r94j3tc.js +221 -0
  12. package/dist/cli/chunk-3r94j3tc.js.map +10 -0
  13. package/dist/cli/chunk-4trphnvy.js +102 -0
  14. package/dist/cli/chunk-4trphnvy.js.map +11 -0
  15. package/dist/cli/chunk-4xyggvgf.js +21 -0
  16. package/dist/cli/chunk-4xyggvgf.js.map +10 -0
  17. package/dist/cli/chunk-5hs6gb7n.js +32 -0
  18. package/dist/cli/chunk-5hs6gb7n.js.map +10 -0
  19. package/dist/cli/chunk-5yvt556e.js +185 -0
  20. package/dist/cli/chunk-5yvt556e.js.map +11 -0
  21. package/dist/cli/chunk-62qsssnh.js +3808 -0
  22. package/dist/cli/chunk-62qsssnh.js.map +36 -0
  23. package/dist/cli/chunk-6kzzpsx8.js +26 -0
  24. package/dist/cli/chunk-6kzzpsx8.js.map +10 -0
  25. package/dist/cli/chunk-8gnpdsn1.js +952 -0
  26. package/dist/cli/chunk-8gnpdsn1.js.map +12 -0
  27. package/dist/cli/chunk-9sh49q0h.js +30 -0
  28. package/dist/cli/chunk-9sh49q0h.js.map +10 -0
  29. package/dist/cli/chunk-aerwpe14.js +2370 -0
  30. package/dist/cli/chunk-aerwpe14.js.map +15 -0
  31. package/dist/cli/chunk-ag1zyr5x.js +176 -0
  32. package/dist/cli/chunk-ag1zyr5x.js.map +10 -0
  33. package/dist/cli/chunk-bawgnt8x.js +277 -0
  34. package/dist/cli/chunk-bawgnt8x.js.map +11 -0
  35. package/dist/cli/chunk-bcy492zc.js +16 -0
  36. package/dist/cli/chunk-bcy492zc.js.map +10 -0
  37. package/dist/cli/chunk-btfr9yvw.js +41 -0
  38. package/dist/cli/chunk-btfr9yvw.js.map +10 -0
  39. package/dist/cli/chunk-cbjnx4s8.js +73 -0
  40. package/dist/cli/chunk-cbjnx4s8.js.map +10 -0
  41. package/dist/cli/chunk-cnvm6k3e.js +96 -0
  42. package/dist/cli/chunk-cnvm6k3e.js.map +10 -0
  43. package/dist/cli/chunk-etsqspj6.js +5170 -0
  44. package/dist/cli/chunk-etsqspj6.js.map +47 -0
  45. package/dist/cli/chunk-ev67ycx0.js +15 -0
  46. package/dist/cli/chunk-ev67ycx0.js.map +10 -0
  47. package/dist/cli/chunk-ey89bjj1.js +209 -0
  48. package/dist/cli/chunk-ey89bjj1.js.map +11 -0
  49. package/dist/cli/chunk-f75cqye8.js +76 -0
  50. package/dist/cli/chunk-f75cqye8.js.map +10 -0
  51. package/dist/cli/chunk-j00ezcg5.js +259 -0
  52. package/dist/cli/chunk-j00ezcg5.js.map +11 -0
  53. package/dist/cli/chunk-jtb45atp.js +467 -0
  54. package/dist/cli/chunk-jtb45atp.js.map +14 -0
  55. package/dist/cli/chunk-m3p3wahd.js +117 -0
  56. package/dist/cli/chunk-m3p3wahd.js.map +10 -0
  57. package/dist/cli/chunk-n0y172hf.js +387 -0
  58. package/dist/cli/chunk-n0y172hf.js.map +12 -0
  59. package/dist/cli/chunk-n4qjabmt.js +1062 -0
  60. package/dist/cli/chunk-n4qjabmt.js.map +25 -0
  61. package/dist/cli/chunk-nyqzjdhj.js +111 -0
  62. package/dist/cli/chunk-nyqzjdhj.js.map +11 -0
  63. package/dist/cli/chunk-pxj10x8y.js +35 -0
  64. package/dist/cli/chunk-pxj10x8y.js.map +10 -0
  65. package/dist/cli/chunk-s4jn7f1q.js +54 -0
  66. package/dist/cli/chunk-s4jn7f1q.js.map +10 -0
  67. package/dist/cli/chunk-s4k1pnvf.js +81 -0
  68. package/dist/cli/chunk-s4k1pnvf.js.map +10 -0
  69. package/dist/cli/chunk-s5e5jt53.js +227 -0
  70. package/dist/cli/chunk-s5e5jt53.js.map +11 -0
  71. package/dist/cli/chunk-sbdqrjbb.js +81 -0
  72. package/dist/cli/chunk-sbdqrjbb.js.map +10 -0
  73. package/dist/cli/chunk-tc89yh2r.js +136 -0
  74. package/dist/cli/chunk-tc89yh2r.js.map +10 -0
  75. package/dist/cli/chunk-vt8fgygt.js +23 -0
  76. package/dist/cli/chunk-vt8fgygt.js.map +10 -0
  77. package/dist/cli/chunk-vv237fp3.js +1002 -0
  78. package/dist/cli/chunk-vv237fp3.js.map +13 -0
  79. package/dist/cli/chunk-vv3f8mb6.js +5314 -0
  80. package/dist/cli/chunk-vv3f8mb6.js.map +58 -0
  81. package/dist/cli/chunk-vxv4x1n8.js +17 -0
  82. package/dist/cli/chunk-vxv4x1n8.js.map +10 -0
  83. package/dist/cli/chunk-wb067mv3.js +758 -0
  84. package/dist/cli/chunk-wb067mv3.js.map +13 -0
  85. package/dist/cli/chunk-wd27zjcz.js +60 -0
  86. package/dist/cli/chunk-wd27zjcz.js.map +10 -0
  87. package/dist/cli/chunk-wkq5tbtq.js +1141 -0
  88. package/dist/cli/chunk-wkq5tbtq.js.map +19 -0
  89. package/dist/cli/chunk-x1vrdjyk.js +1967 -0
  90. package/dist/cli/chunk-x1vrdjyk.js.map +34 -0
  91. package/dist/cli/chunk-x66c5yjn.js +23 -0
  92. package/dist/cli/chunk-x66c5yjn.js.map +10 -0
  93. package/dist/cli/index.js +55 -27587
  94. package/dist/cli/index.js.map +5 -243
  95. package/dist/types/ai/ask-context.d.ts +26 -0
  96. package/dist/types/core/code-fences.d.ts +11 -0
  97. package/dist/types/core/config-input.d.ts +10 -0
  98. package/dist/types/core/package-root.d.ts +1 -1
  99. package/dist/types/core/schema.d.ts +74 -1
  100. package/docs/02-deployment.mdx +1 -1
  101. package/docs/configuration/analytics.mdx +21 -2
  102. package/docs/configuration/ask-ai.mdx +1 -1
  103. package/docs/configuration/customization.mdx +2 -9
  104. package/docs/content/syntax.mdx +1 -1
  105. package/docs/reference/cli.mdx +1 -1
  106. package/package.json +16 -14
  107. package/src/ai/api/handlers.ts +4 -7
  108. package/src/ai/api/paths.ts +8 -0
  109. package/src/ai/api/spec.ts +2 -1
  110. package/src/ai/ask-context.ts +378 -22
  111. package/src/astro/generate.ts +25 -29
  112. package/src/astro/include-hmr.ts +10 -13
  113. package/src/astro/include-refresh.ts +0 -0
  114. package/src/astro/index.ts +6 -1
  115. package/src/astro/integration.ts +269 -53
  116. package/src/astro/module-types.ts +74 -0
  117. package/src/astro/templates.ts +85 -97
  118. package/src/audit/image-size.ts +10 -8
  119. package/src/cli/command-meta.ts +77 -0
  120. package/src/cli/commands/add.ts +2 -4
  121. package/src/cli/commands/audit.ts +2 -4
  122. package/src/cli/commands/build.ts +42 -346
  123. package/src/cli/commands/check.ts +2 -4
  124. package/src/cli/commands/dev.ts +31 -42
  125. package/src/cli/commands/doctor.ts +2 -4
  126. package/src/cli/commands/eject.ts +3 -41
  127. package/src/cli/commands/eval.ts +2 -5
  128. package/src/cli/commands/init.ts +2 -4
  129. package/src/cli/commands/mcp-stdio.ts +2 -5
  130. package/src/cli/commands/preview.ts +3 -5
  131. package/src/cli/commands/sync.ts +2 -4
  132. package/src/cli/commands/translate.ts +2 -5
  133. package/src/cli/commands/validate.ts +2 -4
  134. package/src/cli/commands/version.ts +2 -4
  135. package/src/cli/eject-scripts.ts +0 -45
  136. package/src/cli/host-args.ts +16 -0
  137. package/src/cli/index.ts +84 -35
  138. package/src/cli/lazy-command.ts +47 -0
  139. package/src/components/content/GithubInfo.astro +4 -1
  140. package/src/components/content/mermaid-element.ts +8 -0
  141. package/src/components/layout/Analytics.astro +20 -1
  142. package/src/components/layout/PageLayout.astro +14 -3
  143. package/src/components/layout/ReferenceLayout.astro +15 -4
  144. package/src/components/layout/RootLayout.astro +15 -4
  145. package/src/components/layout/analytics-client.ts +2 -1
  146. package/src/components/layout/page-locale.ts +29 -0
  147. package/src/components/openapi/AsyncApiOperation.astro +5 -3
  148. package/src/components/openapi/Authorization.astro +4 -6
  149. package/src/components/openapi/Bindings.astro +2 -2
  150. package/src/components/openapi/Description.astro +109 -0
  151. package/src/components/openapi/GraphqlFieldsTable.astro +5 -7
  152. package/src/components/openapi/GraphqlOperation.astro +4 -3
  153. package/src/components/openapi/GraphqlType.astro +4 -6
  154. package/src/components/openapi/ParametersTable.astro +5 -7
  155. package/src/components/openapi/RequestBody.astro +2 -4
  156. package/src/components/openapi/Responses.astro +4 -3
  157. package/src/components/openapi/SchemaProperty.astro +11 -6
  158. package/src/components/openapi/description.ts +91 -0
  159. package/src/core/api-name.ts +18 -0
  160. package/src/core/code-fences.ts +48 -0
  161. package/src/core/config-input.ts +10 -0
  162. package/src/core/content-assets.ts +3 -7
  163. package/src/core/includes.ts +3 -7
  164. package/src/core/package-root.ts +1 -1
  165. package/src/core/schema.ts +27 -0
  166. package/src/core/sources/normalize.ts +2 -37
  167. package/src/core/sources/obsidian.ts +3 -2
  168. package/src/core/svg-dimensions.ts +97 -0
  169. package/src/core/version-cut.ts +2 -2
  170. package/src/deploy/artifacts.ts +370 -0
  171. package/src/deploy/cloudflare-negotiation.ts +97 -32
  172. package/src/deploy/function-bundle.ts +66 -20
  173. package/src/deploy/sitemap.ts +6 -0
  174. package/src/deploy/vercel-negotiation.ts +8 -30
  175. package/src/og/card.ts +6 -12
  176. package/src/openapi/render-mdx.ts +9 -5
  177. package/src/registry/eject.ts +0 -2
  178. package/src/theme/entry.ts +9 -2
@@ -0,0 +1,758 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ EXAMPLE_SCAN_GLOB,
4
+ buildAskData,
5
+ buildMcpDiscovery,
6
+ buildMcpServerCard,
7
+ buildReferenceFiles,
8
+ buildRuntimeData,
9
+ buildThemeCss,
10
+ collectStaged,
11
+ detectNeedsReact,
12
+ detectUsesMath,
13
+ discoverExamples,
14
+ discoverIslands,
15
+ exampleMarkdownLookup,
16
+ exampleScanRoots,
17
+ examplesEntryTemplate,
18
+ planComponentSlots,
19
+ rebaseSourceDirectives,
20
+ tailwindEntryTemplate,
21
+ twoslashCss
22
+ } from "./chunk-62qsssnh.js";
23
+ import {
24
+ buildMcpData,
25
+ buildRawMarkdown,
26
+ buildRssFeeds,
27
+ buildSearchDocuments,
28
+ renderRssFeed
29
+ } from "./chunk-n4qjabmt.js";
30
+ import {
31
+ customOgRoutes,
32
+ discoverPages,
33
+ routeIsTaken
34
+ } from "./chunk-sbdqrjbb.js";
35
+ import {
36
+ askComponentTemplate,
37
+ askEndpointTemplate,
38
+ astroConfigTemplate,
39
+ catchAllPageTemplate,
40
+ changelogIndexTemplate,
41
+ contentConfigTemplate,
42
+ exampleMapTemplate,
43
+ exampleSlug,
44
+ exampleWrapperTemplate,
45
+ examplesPageTemplate,
46
+ islandMapTemplate,
47
+ islandWrapperTemplate,
48
+ mcpEndpointTemplate,
49
+ mcpPageFile,
50
+ mixedbreadSearchEndpointTemplate,
51
+ notFoundPageTemplate,
52
+ ogEndpointTemplate,
53
+ rawMarkdownEndpointTemplate,
54
+ resolveAskBackend,
55
+ rssEndpointTemplate,
56
+ runtimeTsconfigTemplate,
57
+ searchClientTemplate,
58
+ searchEndpointTemplate,
59
+ staticJsonEndpointTemplate
60
+ } from "./chunk-aerwpe14.js";
61
+ import {
62
+ servesStaticIndex
63
+ } from "./chunk-wd27zjcz.js";
64
+ import {
65
+ buildIncludeGraph,
66
+ isOpenApiSource,
67
+ scanProject
68
+ } from "./chunk-etsqspj6.js";
69
+ import {
70
+ hasScalarReferences
71
+ } from "./chunk-vv3f8mb6.js";
72
+ import {
73
+ getBlumeVersion
74
+ } from "./chunk-4xyggvgf.js";
75
+ import {
76
+ packageRoot
77
+ } from "./chunk-6kzzpsx8.js";
78
+
79
+ // src/registry/eject.ts
80
+ import { existsSync } from "node:fs";
81
+ import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
82
+ import { join, relative } from "pathe";
83
+ var toPosix = (path) => path.split("\\").join("/");
84
+ var LOCAL_BLUME_SOURCE = "../../node_modules/blume/src/**/*.{astro,ts,tsx}";
85
+ var blumeSourceGlob = (root, genDir, resolveBlumeRoot = packageRoot) => {
86
+ if (existsSync(join(root, "node_modules", "blume"))) {
87
+ return LOCAL_BLUME_SOURCE;
88
+ }
89
+ try {
90
+ const src = join(resolveBlumeRoot(), "src");
91
+ return `${toPosix(relative(genDir, src))}/**/*.{astro,ts,tsx}`;
92
+ } catch {
93
+ console.warn('blume: could not locate the installed blume package; src/generated/app.css keeps its default `@source "../../node_modules/blume/..."` glob. If blume is hoisted elsewhere, point that glob at its install location or Blume\'s utility classes will be missing.');
94
+ return LOCAL_BLUME_SOURCE;
95
+ }
96
+ };
97
+ var ejectOpenApiData = (project) => {
98
+ const source = project.sources.find(isOpenApiSource);
99
+ return source ? source.openApiData() : {};
100
+ };
101
+ var askFiles = async (project, srcDir, genDir) => {
102
+ const { ask } = project.config.ai;
103
+ if (!(ask?.enabled && !ask.endpoint)) {
104
+ const endpointPath = join(srcDir, "pages", "api", "ask.ts");
105
+ if (existsSync(endpointPath)) {
106
+ const content = await readFile(endpointPath, "utf-8");
107
+ if (content.startsWith("// Generated by Blume. Do not edit.")) {
108
+ await rm(endpointPath, { force: true });
109
+ }
110
+ }
111
+ await rm(join(genDir, "ask-data.json"), { force: true });
112
+ return [];
113
+ }
114
+ const grounded = ask.provider !== "inkeep";
115
+ const files = [
116
+ {
117
+ content: askEndpointTemplate(resolveAskBackend(ask), grounded, {
118
+ instructions: ask.instructions,
119
+ retrieval: ask.retrieval
120
+ }),
121
+ path: join(srcDir, "pages", "api", "ask.ts")
122
+ }
123
+ ];
124
+ if (grounded) {
125
+ files.push({
126
+ content: `${JSON.stringify(await buildAskData(project))}
127
+ `,
128
+ path: join(genDir, "ask-data.json")
129
+ });
130
+ }
131
+ return files;
132
+ };
133
+ var hostsMcp = (project, userPages) => project.config.ai.mcp.enabled && !routeIsTaken(userPages, project.graph.pages, project.config.ai.mcp.route);
134
+ var mcpDiscoveryPages = (project, userPages) => hostsMcp(project, userPages) ? [
135
+ {
136
+ entrypoint: "src/blume-mcp/discovery.ts",
137
+ pattern: "/.well-known/mcp.json"
138
+ },
139
+ {
140
+ entrypoint: "src/blume-mcp/server-card.ts",
141
+ pattern: "/.well-known/mcp/server-card.json"
142
+ }
143
+ ] : [];
144
+ var mcpFiles = async (project, userPages, srcDir, genDir) => {
145
+ if (!hostsMcp(project, userPages)) {
146
+ return [];
147
+ }
148
+ const { route } = project.config.ai.mcp;
149
+ const data = await buildMcpData(project);
150
+ const discoveryInput = {
151
+ base: data.base,
152
+ name: data.name,
153
+ route,
154
+ site: data.site,
155
+ version: data.version
156
+ };
157
+ return [
158
+ {
159
+ content: `${JSON.stringify(data)}
160
+ `,
161
+ path: join(genDir, "mcp-data.json")
162
+ },
163
+ {
164
+ content: mcpEndpointTemplate(),
165
+ path: join(srcDir, "pages", mcpPageFile(route))
166
+ },
167
+ {
168
+ content: staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput)),
169
+ path: join(srcDir, "blume-mcp", "discovery.ts")
170
+ },
171
+ {
172
+ content: staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)),
173
+ path: join(srcDir, "blume-mcp", "server-card.ts")
174
+ }
175
+ ];
176
+ };
177
+ var changelogFiles = (project, userPages, srcDir, options) => {
178
+ const hasChangelog = project.graph.pages.some((page) => page.contentType === "changelog" && !(page.meta.draft || page.meta.sidebar.hidden));
179
+ const hasChangelogSource = (project.config.content.sources ?? []).some((source) => source.type === "github-releases");
180
+ if (!(hasChangelog || hasChangelogSource) || routeIsTaken(userPages, project.graph.pages, "/changelog")) {
181
+ return [];
182
+ }
183
+ return [
184
+ {
185
+ content: changelogIndexTemplate(options),
186
+ path: join(srcDir, "pages", "changelog.astro")
187
+ }
188
+ ];
189
+ };
190
+ var readUserCss = async (file, genDir) => {
191
+ if (!(file && existsSync(file))) {
192
+ return "";
193
+ }
194
+ const css = await readFile(file, "utf-8");
195
+ return rebaseSourceDirectives(css, { from: file, to: genDir });
196
+ };
197
+ var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
198
+ {
199
+ content: examplesPageTemplate(),
200
+ path: join(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
201
+ }
202
+ ] : [];
203
+ var ejectIntegrationBridge = (config, root, configFile) => config.integrations.length > 0 && configFile ? { configFile: toPosix(relative(root, configFile)) } : undefined;
204
+ var eject = async (root) => {
205
+ const project = await scanProject(root, { mode: "build" });
206
+ const { context, config } = project;
207
+ const srcDir = join(root, "src");
208
+ const genDir = join(srcDir, "generated");
209
+ const askEnabled = config.ai.ask?.enabled ?? false;
210
+ const exportPdf = config.export.pdf;
211
+ const exportEpub = config.export.epub;
212
+ const examples = await discoverExamples(root, config.examples.source);
213
+ project.examples = exampleMarkdownLookup(examples.examples);
214
+ const [
215
+ pages,
216
+ needsReactRaw,
217
+ usesMath,
218
+ userTheme,
219
+ userExamplesCss,
220
+ rawMarkdown,
221
+ islands
222
+ ] = await Promise.all([
223
+ context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
224
+ detectNeedsReact(root),
225
+ detectUsesMath(root),
226
+ readUserCss(context.themeFile, genDir),
227
+ readUserCss(config.examples.css ? join(root, config.examples.css) : null, genDir),
228
+ buildRawMarkdown(project),
229
+ discoverIslands(root)
230
+ ]);
231
+ const frameworks = new Set([
232
+ ...islands.islands.map((island) => island.framework),
233
+ ...examples.examples.map((example) => example.framework)
234
+ ]);
235
+ const needsReact = needsReactRaw || askEnabled || frameworks.has("react");
236
+ const needsVue = frameworks.has("vue");
237
+ const needsSvelte = frameworks.has("svelte");
238
+ const relContext = {
239
+ ...context,
240
+ contentRoot: toPosix(relative(root, context.contentRoot)),
241
+ outDir: ".",
242
+ root: "."
243
+ };
244
+ const componentsImport = context.componentsFile ? `../../${toPosix(relative(root, context.componentsFile))}` : null;
245
+ const relPages = [
246
+ ...pages.map((page) => ({
247
+ entrypoint: toPosix(relative(root, page.entrypoint)),
248
+ pattern: page.pattern
249
+ })),
250
+ ...mcpDiscoveryPages(project, pages)
251
+ ];
252
+ const staged = collectStaged(project);
253
+ const hasStaged = staged.size > 0;
254
+ const stagedDir = "blume-staged";
255
+ const files = [
256
+ {
257
+ content: astroConfigTemplate({
258
+ askPath: "./src/generated/Ask.astro",
259
+ config,
260
+ contentRoot: relContext.contentRoot,
261
+ contentRoutes: project.manifest.routes.map((route) => route.path),
262
+ context: relContext,
263
+ examplesPath: "./src/generated/examples.ts",
264
+ examplesThemePath: "./src/generated/examples.css",
265
+ generatedModulesDir: "./src/generated",
266
+ integrationBridge: ejectIntegrationBridge(config, root, context.configFile),
267
+ needsReact,
268
+ needsSvelte,
269
+ needsVue,
270
+ pages: relPages,
271
+ searchClientPath: "./src/generated/search-client.ts",
272
+ themePath: "./src/generated/app.css"
273
+ }),
274
+ path: join(root, "astro.config.mjs")
275
+ },
276
+ {
277
+ content: runtimeTsconfigTemplate(),
278
+ path: join(root, "tsconfig.json"),
279
+ skipIfExists: true
280
+ },
281
+ {
282
+ content: contentConfigTemplate({
283
+ config,
284
+ context: relContext,
285
+ staged: hasStaged,
286
+ stagedBase: stagedDir
287
+ }),
288
+ path: join(srcDir, "content.config.ts")
289
+ },
290
+ {
291
+ content: catchAllPageTemplate({
292
+ exportEpub,
293
+ exportPdf,
294
+ mathEnabled: usesMath,
295
+ needsReact
296
+ }),
297
+ path: join(srcDir, "pages", "[...slug].astro")
298
+ },
299
+ {
300
+ content: planComponentSlots(componentsImport, null).module,
301
+ path: join(genDir, "components.ts")
302
+ },
303
+ {
304
+ content: islandMapTemplate(islands.islands),
305
+ path: join(genDir, "islands.ts")
306
+ },
307
+ {
308
+ content: exampleMapTemplate(examples.examples, config.basePath),
309
+ path: join(genDir, "examples.ts")
310
+ },
311
+ {
312
+ content: examplesEntryTemplate({
313
+ configTokens: buildThemeCss(config.theme),
314
+ sources: exampleScanRoots(root, examples.dir).map((dir) => `${relative(genDir, dir)}/${EXAMPLE_SCAN_GLOB}`),
315
+ userCss: userExamplesCss
316
+ }),
317
+ path: join(genDir, "examples.css")
318
+ },
319
+ {
320
+ content: tailwindEntryTemplate({
321
+ configTokens: buildThemeCss(config.theme),
322
+ sources: [
323
+ blumeSourceGlob(root, genDir),
324
+ "../../**/*.{astro,mdx,ts,tsx}"
325
+ ],
326
+ twoslashCss: twoslashCss(),
327
+ userTheme
328
+ }),
329
+ path: join(genDir, "app.css")
330
+ },
331
+ { content: buildRuntimeData(project), path: join(genDir, "data.json") },
332
+ {
333
+ content: askComponentTemplate(askEnabled),
334
+ path: join(genDir, "Ask.astro")
335
+ },
336
+ {
337
+ content: `${JSON.stringify(ejectOpenApiData(project))}
338
+ `,
339
+ path: join(genDir, "openapi.json")
340
+ },
341
+ {
342
+ content: `${JSON.stringify(rawMarkdown)}
343
+ `,
344
+ path: join(genDir, "raw-markdown.json")
345
+ },
346
+ {
347
+ content: `${JSON.stringify(buildIncludeGraph(project.graph.pages))}
348
+ `,
349
+ path: join(genDir, "includes.json")
350
+ },
351
+ {
352
+ content: rawMarkdownEndpointTemplate("md"),
353
+ path: join(srcDir, "pages", "[...slug].md.ts")
354
+ },
355
+ {
356
+ content: rawMarkdownEndpointTemplate("mdx"),
357
+ path: join(srcDir, "pages", "[...slug].mdx.ts")
358
+ }
359
+ ];
360
+ if (askEnabled) {
361
+ files.push(...await askFiles(project, srcDir, genDir));
362
+ }
363
+ if (config.seo.og.enabled) {
364
+ files.push({
365
+ content: ogEndpointTemplate(customOgRoutes(pages, config.title, config.seo.og.titles), { pageDescriptions: config.seo.og.description !== false }),
366
+ path: join(srcDir, "pages", "og", "[...slug].png.ts")
367
+ });
368
+ }
369
+ files.push(...await mcpFiles(project, pages, srcDir, genDir), ...changelogFiles(project, pages, srcDir, {
370
+ exportEpub,
371
+ exportPdf,
372
+ needsReact,
373
+ staged: hasStaged
374
+ }));
375
+ if (!routeIsTaken(pages, project.graph.pages, "/404")) {
376
+ files.push({
377
+ content: notFoundPageTemplate(),
378
+ path: join(srcDir, "pages", "404.astro")
379
+ });
380
+ }
381
+ files.push({
382
+ content: searchClientTemplate(config),
383
+ path: join(genDir, "search-client.ts")
384
+ });
385
+ if (servesStaticIndex(config.search.provider)) {
386
+ const documents = await buildSearchDocuments(project);
387
+ files.push({
388
+ content: `${JSON.stringify(documents)}
389
+ `,
390
+ path: join(genDir, "search.json")
391
+ }, {
392
+ content: searchEndpointTemplate(),
393
+ path: join(srcDir, "pages", "blume-search.json.ts")
394
+ });
395
+ }
396
+ if (config.search.provider === "mixedbread") {
397
+ files.push({
398
+ content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
399
+ path: join(srcDir, "pages", "api", "search.ts")
400
+ });
401
+ }
402
+ const feeds = buildRssFeeds(project);
403
+ if (feeds.length > 0) {
404
+ const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
405
+ files.push({
406
+ content: `${JSON.stringify(feedXml)}
407
+ `,
408
+ path: join(genDir, "rss.json")
409
+ }, {
410
+ content: rssEndpointTemplate(),
411
+ path: join(srcDir, "pages", "[section]", "rss.xml.ts")
412
+ });
413
+ }
414
+ const warnings = [];
415
+ if (hasScalarReferences(config)) {
416
+ const references = await buildReferenceFiles({
417
+ config,
418
+ contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
419
+ root
420
+ });
421
+ warnings.push(...references.warnings);
422
+ for (const file of references.files) {
423
+ files.push({
424
+ content: file.content,
425
+ path: join(srcDir, "pages", file.pagePath)
426
+ });
427
+ }
428
+ }
429
+ files.push(...islands.islands.map((island) => ({
430
+ content: islandWrapperTemplate(island),
431
+ path: join(genDir, "islands", `${island.name}.astro`)
432
+ })), ...examples.examples.map((example) => ({
433
+ content: exampleWrapperTemplate(example),
434
+ path: join(genDir, "examples", `${exampleSlug(example.path)}.astro`)
435
+ })), ...examplesPreviewFiles(srcDir, config.basePath, examples.examples.length > 0));
436
+ for (const [entryId, content] of staged) {
437
+ files.push({ content, path: join(root, stagedDir, entryId) });
438
+ }
439
+ const written = files.filter((file) => !(file.skipIfExists && existsSync(file.path)));
440
+ await Promise.all(written.map(async (file) => {
441
+ await mkdir(join(file.path, ".."), { recursive: true });
442
+ await writeFile(file.path, file.content, "utf-8");
443
+ }));
444
+ const assetsSrc = join(context.outDir, "public", "blume-assets");
445
+ if (existsSync(assetsSrc)) {
446
+ await cp(assetsSrc, join(root, "public", "blume-assets"), {
447
+ recursive: true
448
+ });
449
+ }
450
+ await rm(context.outDir, { force: true, recursive: true });
451
+ return { files: written.map((file) => file.path), warnings };
452
+ };
453
+
454
+ // src/cli/eject-scripts.ts
455
+ import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
456
+ import { join as join2 } from "pathe";
457
+ var updatePackageScripts = async (root) => {
458
+ const pkgPath = join2(root, "package.json");
459
+ let pkg;
460
+ try {
461
+ pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
462
+ } catch {
463
+ return;
464
+ }
465
+ const scripts = pkg.scripts ?? {};
466
+ pkg.scripts = {
467
+ ...scripts,
468
+ build: "astro build",
469
+ dev: "astro dev",
470
+ preview: "astro preview"
471
+ };
472
+ await writeFile2(pkgPath, `${JSON.stringify(pkg, null, 2)}
473
+ `, "utf-8");
474
+ };
475
+
476
+ // src/cli/init/scaffold.ts
477
+ import { existsSync as existsSync2 } from "node:fs";
478
+ import { mkdir as mkdir2, writeFile as writeFile3 } from "node:fs/promises";
479
+ import { detect } from "package-manager-detector/detect";
480
+ import { basename, dirname, isAbsolute, join as join3, relative as relative2 } from "pathe";
481
+
482
+ // src/core/package-json.ts
483
+ var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
484
+ var blumePackageJson = (name, extraDeps = {}) => {
485
+ const dependencies = Object.entries({
486
+ blume: `^${getBlumeVersion()}`,
487
+ ...extraDeps
488
+ }).toSorted(([a], [b]) => a < b ? -1 : 1).map(([dep, range]) => ` ${JSON.stringify(dep)}: ${JSON.stringify(range)}`).join(`,
489
+ `);
490
+ return `{
491
+ "name": ${JSON.stringify(name)},
492
+ "private": true,
493
+ "type": "module",
494
+ "scripts": {
495
+ "dev": "blume dev",
496
+ "build": "blume build",
497
+ "doctor": "blume doctor"
498
+ },
499
+ "dependencies": {
500
+ ${dependencies}
501
+ }
502
+ }
503
+ `;
504
+ };
505
+
506
+ // src/cli/init/scaffold.ts
507
+ var TEMPLATES = ["docs", "api", "sdk", "changelog"];
508
+ var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
509
+ var SOURCE_KINDS = [
510
+ "filesystem",
511
+ "obsidian",
512
+ "github-releases",
513
+ "notion",
514
+ "sanity",
515
+ "mdx-remote"
516
+ ];
517
+ var page = (title, description, body) => `---
518
+ title: ${title}
519
+ description: ${description}
520
+ ---
521
+
522
+ ${body}
523
+ `;
524
+ var STARTERS = {
525
+ api: {
526
+ configExtra: `
527
+ openapi: {
528
+ enabled: true,
529
+ route: "/api",
530
+ sources: [
531
+ {
532
+ label: "Petstore",
533
+ spec: "https://petstore3.swagger.io/api/v3/openapi.json",
534
+ },
535
+ ],
536
+ },`,
537
+ files: (dir) => [
538
+ {
539
+ content: page("API Reference", "Explore the API.", "# API Reference\n\nYour OpenAPI spec renders at [`/api`](/api). Point `openapi.sources` at your own spec in `blume.config.ts`."),
540
+ path: join3(dir, "index.mdx")
541
+ }
542
+ ]
543
+ },
544
+ changelog: {
545
+ configExtra: `
546
+ navigation: {
547
+ tabs: [
548
+ { label: "Docs", path: "/" },
549
+ { label: "Changelog", path: "/changelog" },
550
+ ],
551
+ },`,
552
+ files: (dir) => [
553
+ {
554
+ content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
555
+ path: join3(dir, "index.mdx")
556
+ },
557
+ {
558
+ content: `---
559
+ title: v1.0.0
560
+ type: changelog
561
+ date: 2026-01-01
562
+ ---
563
+
564
+ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
565
+ `,
566
+ path: join3(dir, "changelog", "v1-0-0.mdx")
567
+ }
568
+ ]
569
+ },
570
+ docs: {
571
+ configExtra: "",
572
+ files: (dir) => [
573
+ {
574
+ content: page("Introduction", "Welcome to your new Blume docs.", `# Introduction
575
+
576
+ Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
577
+
578
+ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
579
+ path: join3(dir, "index.mdx")
580
+ }
581
+ ]
582
+ },
583
+ sdk: {
584
+ configExtra: "",
585
+ files: (dir) => [
586
+ {
587
+ content: page("Introduction", "Get started with the SDK.", `# Introduction
588
+
589
+ Install the SDK and make your first call. See [Installation](/installation).`),
590
+ path: join3(dir, "index.mdx")
591
+ },
592
+ {
593
+ content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
594
+ path: join3(dir, "installation.mdx")
595
+ }
596
+ ]
597
+ }
598
+ };
599
+ var commandsFor = (pm) => ({
600
+ build: pm === "npm" || pm === "bun" ? `${pm} run build` : `${pm} build`,
601
+ dev: pm === "npm" ? "npm run dev" : `${pm} dev`,
602
+ exec: { bun: "bunx", npm: "npx", pnpm: "pnpm exec", yarn: "yarn" }[pm],
603
+ install: `${pm} install`
604
+ });
605
+ var isPackageManager = (value) => PACKAGE_MANAGERS.some((pm) => pm === value);
606
+ var detectPackageManager = (userAgent) => {
607
+ const name = userAgent?.split("/")[0];
608
+ return name !== undefined && isPackageManager(name) ? name : "npm";
609
+ };
610
+ var detectProjectPackageManager = async (root) => {
611
+ const detected = await detect({ cwd: root });
612
+ const name = detected?.name;
613
+ return name !== undefined && isPackageManager(name) ? name : detectPackageManager(process.env.npm_config_user_agent);
614
+ };
615
+ var validateContentDir = (root, dir) => isAbsolute(dir) || relative2(root, join3(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
616
+ var titleize = (raw) => {
617
+ const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
618
+ return words.length === 0 ? "My Docs" : words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
619
+ };
620
+ var needsExplicitSources = (sources) => sources.some((source) => source !== "filesystem");
621
+ var SOURCE_SNIPPETS = {
622
+ "github-releases": ` // Changelog entries from GitHub Releases. Private repos read
623
+ // GITHUB_TOKEN from the environment.
624
+ {
625
+ type: "github-releases",
626
+ owner: "your-org",
627
+ repo: "your-repo",
628
+ prefix: "changelog",
629
+ },`,
630
+ "mdx-remote": ` // MDX fetched from a GitHub repo. Private repos read GITHUB_TOKEN
631
+ // from the environment.
632
+ {
633
+ type: "mdx-remote",
634
+ github: { owner: "your-org", repo: "your-repo", path: "docs" },
635
+ prefix: "remote",
636
+ },`,
637
+ notion: ` // Pages from a Notion database. Reads NOTION_TOKEN from the environment.
638
+ {
639
+ type: "notion",
640
+ database: "your-database-id",
641
+ prefix: "notion",
642
+ },`,
643
+ obsidian: ` // An Obsidian vault, read in place. No export step, and no
644
+ // generated notes in your repo. Point \`vault\` at your vault directory,
645
+ // relative to this config file.
646
+ {
647
+ type: "obsidian",
648
+ vault: "vault",
649
+ prefix: "notes",
650
+ },`,
651
+ sanity: ` // Documents from a Sanity dataset. Private datasets read SANITY_TOKEN
652
+ // from the environment.
653
+ {
654
+ type: "sanity",
655
+ projectId: "your-project-id",
656
+ dataset: "production",
657
+ query: \`*[_type == "doc"]\`,
658
+ prefix: "sanity",
659
+ },`
660
+ };
661
+ var contentBlockFor = (answers) => {
662
+ const sources = answers.sources.length === 0 ? ["filesystem"] : answers.sources;
663
+ if (!needsExplicitSources(sources)) {
664
+ return answers.contentDir === "docs" ? "" : `
665
+ content: {
666
+ root: ${JSON.stringify(answers.contentDir)},
667
+ },`;
668
+ }
669
+ const entries = SOURCE_KINDS.filter((kind) => sources.includes(kind)).map((kind) => kind === "filesystem" ? ` { type: "filesystem", root: ${JSON.stringify(answers.contentDir)} },` : SOURCE_SNIPPETS[kind]);
670
+ return `
671
+ content: {
672
+ sources: [
673
+ ${entries.join(`
674
+ `)}
675
+ ],
676
+ },`;
677
+ };
678
+ var buildConfig = (answers) => `import { defineConfig } from "blume";
679
+
680
+ export default defineConfig({
681
+ title: ${JSON.stringify(answers.title)},
682
+ description: "Documentation powered by Blume.",${STARTERS[answers.template].configExtra}${contentBlockFor(answers)}
683
+ });
684
+ `;
685
+ var extraDepsFor = (sources) => {
686
+ const deps = {};
687
+ if (sources.includes("notion")) {
688
+ deps["@notionhq/client"] = "^2.2.15";
689
+ }
690
+ if (sources.includes("sanity")) {
691
+ deps["@sanity/client"] = "^7.25.0";
692
+ }
693
+ return deps;
694
+ };
695
+ var buildPlan = (root, answers) => {
696
+ const files = [
697
+ {
698
+ content: blumePackageJson(toPackageName(basename(root)), extraDepsFor(answers.sources)),
699
+ path: join3(root, "package.json")
700
+ },
701
+ { content: buildConfig(answers), path: join3(root, "blume.config.ts") }
702
+ ];
703
+ if (answers.sources.length === 0 || answers.sources.includes("filesystem")) {
704
+ files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path: join3(root, file.path) })));
705
+ }
706
+ if (answers.sources.includes("obsidian")) {
707
+ files.push({
708
+ content: "# Welcome\n\nThis folder is read by Blume's `obsidian` source. Open it as a vault in Obsidian and write notes — `[[Wikilinks]]` become site links.\n",
709
+ path: join3(root, "vault", "Welcome.md")
710
+ });
711
+ }
712
+ return files;
713
+ };
714
+ var writeFileSafe = async (file, log) => {
715
+ if (existsSync2(file.path)) {
716
+ log.info(`Skipped existing ${file.path}`);
717
+ return false;
718
+ }
719
+ await mkdir2(dirname(file.path), { recursive: true });
720
+ await writeFile3(file.path, file.content, "utf-8");
721
+ log.success(`Created ${file.path}`);
722
+ return true;
723
+ };
724
+ var applyPlan = async (files, log) => {
725
+ const created = await Promise.all(files.map((file) => writeFileSafe(file, log)));
726
+ const createdPackage = files.some((file, index) => created[index] && basename(file.path) === "package.json");
727
+ return { createdPackage };
728
+ };
729
+ var envVarsFor = (sources) => [
730
+ ["GITHUB_TOKEN", ["github-releases", "mdx-remote"]],
731
+ ["NOTION_TOKEN", ["notion"]],
732
+ ["SANITY_TOKEN", ["sanity"]]
733
+ ].filter(([, kinds]) => kinds.some((kind) => sources.includes(kind))).map(([envVar]) => envVar);
734
+ var nextSteps = (answers, createdPackage) => {
735
+ const commands = commandsFor(answers.packageManager);
736
+ const lines = [];
737
+ if (answers.directory !== ".") {
738
+ lines.push(`cd ${answers.directory}`);
739
+ }
740
+ if (createdPackage) {
741
+ lines.push(commands.install);
742
+ }
743
+ lines.push(commands.dev);
744
+ const envVars = envVarsFor(answers.sources);
745
+ const auth = envVars.length > 0 ? `
746
+ Set ${envVars.join(" and ")} in .env.local so your sources can authenticate.
747
+ ` : "";
748
+ return `Next steps:
749
+
750
+ ${lines.join(`
751
+ `)}
752
+ ${auth}`;
753
+ };
754
+
755
+ export { eject, updatePackageScripts, TEMPLATES, PACKAGE_MANAGERS, commandsFor, detectPackageManager, detectProjectPackageManager, validateContentDir, titleize, buildPlan, applyPlan, nextSteps };
756
+
757
+ //# debugId=2D42D4BB847B62D564756E2164756E21
758
+ //# sourceMappingURL=chunk-wb067mv3.js.map