blume 0.4.0 → 0.5.0

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 (67) hide show
  1. package/dist/cli/index.js +1137 -722
  2. package/dist/cli/index.js.map +28 -23
  3. package/dist/types/core/data.d.ts +2 -0
  4. package/dist/types/core/project.d.ts +12 -2
  5. package/dist/types/core/schema.d.ts +154 -15
  6. package/dist/types/core/types.d.ts +7 -0
  7. package/docs/advanced/api-reference.mdx +33 -23
  8. package/docs/advanced/bridge.mdx +74 -0
  9. package/docs/advanced/meta.ts +8 -1
  10. package/docs/advanced/migrate.mdx +119 -0
  11. package/docs/configuration/index.mdx +1 -1
  12. package/docs/content/components.mdx +55 -2
  13. package/docs/content/i18n.mdx +1 -1
  14. package/docs/content/syntax.mdx +2 -2
  15. package/docs/index.mdx +2 -2
  16. package/docs/reference/cli.mdx +29 -1
  17. package/docs/reference/frontmatter.mdx +5 -0
  18. package/package.json +11 -1
  19. package/src/astro/generate.ts +18 -8
  20. package/src/astro/templates.ts +28 -4
  21. package/src/cli/commands/build.ts +107 -63
  22. package/src/cli/commands/check.ts +20 -0
  23. package/src/cli/dev-lock.ts +13 -5
  24. package/src/cli/prepare.ts +3 -0
  25. package/src/components/BlumePage.astro +6 -0
  26. package/src/components/Icon.astro +13 -10
  27. package/src/components/content/ApiField.astro +75 -0
  28. package/src/components/content/ParamField.astro +39 -0
  29. package/src/components/content/RequestField.astro +23 -0
  30. package/src/components/content/ResponseField.astro +23 -0
  31. package/src/components/content/Step.astro +1 -1
  32. package/src/components/layout/Breadcrumbs.astro +7 -2
  33. package/src/components/layout/NavTree.astro +24 -8
  34. package/src/components/layout/RootLayout.astro +56 -34
  35. package/src/components/layout/Search.astro +1 -1
  36. package/src/components/openapi/ApiOverview.astro +84 -0
  37. package/src/components/openapi/MethodBadge.astro +28 -0
  38. package/src/components/openapi/Operation.astro +140 -0
  39. package/src/components/openapi/ParametersTable.astro +97 -0
  40. package/src/components/openapi/RequestBody.astro +58 -0
  41. package/src/components/openapi/RequestPanel.astro +169 -0
  42. package/src/components/openapi/Responses.astro +91 -0
  43. package/src/components/openapi/SchemaProperty.astro +118 -0
  44. package/src/components/openapi/SchemaTable.astro +86 -0
  45. package/src/components/openapi/helpers.ts +238 -0
  46. package/src/components/openapi/panel.ts +59 -0
  47. package/src/components/openapi/snippets.ts +201 -0
  48. package/src/core/builtin-tags.ts +5 -0
  49. package/src/core/data.ts +2 -0
  50. package/src/core/project-graph.ts +5 -1
  51. package/src/core/project.ts +25 -3
  52. package/src/core/schema.ts +47 -6
  53. package/src/core/sources/mintlify.ts +1 -1
  54. package/src/core/sources/resolve.ts +28 -6
  55. package/src/core/types.ts +7 -0
  56. package/src/migrate/mintlify/config.ts +153 -1
  57. package/src/migrate/mintlify/content.ts +8 -2
  58. package/src/migrate/mintlify/index.ts +58 -1
  59. package/src/openapi/model.ts +174 -0
  60. package/src/openapi/parse.ts +48 -0
  61. package/src/openapi/references.ts +164 -0
  62. package/src/openapi/render-mdx.ts +76 -0
  63. package/src/openapi/scalar.ts +15 -103
  64. package/src/openapi/source.ts +140 -0
  65. package/src/registry/eject.ts +15 -2
  66. package/src/theme/chrome-icons.ts +22 -0
  67. package/src/theme/icons.ts +151 -161
package/dist/cli/index.js CHANGED
@@ -513,11 +513,11 @@ Next steps:
513
513
  });
514
514
 
515
515
  // src/cli/commands/build.ts
516
- import { existsSync as existsSync16 } from "node:fs";
517
- import { readdir, stat, writeFile as writeFile6 } from "node:fs/promises";
516
+ import { existsSync as existsSync17 } from "node:fs";
517
+ import { readdir, stat, writeFile as writeFile7 } from "node:fs/promises";
518
518
  import { build } from "astro";
519
519
  import { defineCommand as defineCommand2 } from "citty";
520
- import { join as join26 } from "pathe";
520
+ import { join as join28 } from "pathe";
521
521
 
522
522
  // src/core/frontmatter.ts
523
523
  import baseMatter from "gray-matter";
@@ -608,6 +608,29 @@ var buildLlmsFiles = async (project) => ({
608
608
  index: buildIndex(project)
609
609
  });
610
610
 
611
+ // src/core/gitignore.ts
612
+ import { existsSync as existsSync3 } from "node:fs";
613
+ import { readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
614
+ import { join as join5 } from "pathe";
615
+ var gitignoreKey = (line) => line.trim().replace(/\/+$/u, "");
616
+ var ensureGitignore = async (root, entries) => {
617
+ const path = join5(root, ".gitignore");
618
+ const existing = existsSync3(path) ? await readFile3(path, "utf-8") : "";
619
+ const present = new Set(existing.split(`
620
+ `).map(gitignoreKey).filter(Boolean));
621
+ const added = entries.filter((entry) => !present.has(gitignoreKey(entry)));
622
+ if (added.length === 0) {
623
+ return [];
624
+ }
625
+ const gap = existing.length > 0 && !existing.endsWith(`
626
+ `) ? `
627
+ ` : "";
628
+ await writeFile2(path, `${existing}${gap}${added.join(`
629
+ `)}
630
+ `, "utf-8");
631
+ return added;
632
+ };
633
+
611
634
  // src/search/providers.ts
612
635
  var SEARCH_PROVIDERS = {
613
636
  algolia: {
@@ -739,7 +762,7 @@ ${urls.join(`
739
762
  };
740
763
 
741
764
  // src/search/build.ts
742
- import { join as join5 } from "pathe";
765
+ import { join as join6 } from "pathe";
743
766
  var buildSearchIndex = async (outDir) => {
744
767
  const pagefind = await import("pagefind");
745
768
  const { index } = await pagefind.createIndex({});
@@ -747,7 +770,7 @@ var buildSearchIndex = async (outDir) => {
747
770
  throw new Error("Failed to create Pagefind index.");
748
771
  }
749
772
  const result = await index.addDirectory({ path: outDir });
750
- await index.writeFiles({ outputPath: join5(outDir, "pagefind") });
773
+ await index.writeFiles({ outputPath: join6(outDir, "pagefind") });
751
774
  await pagefind.close();
752
775
  return result.page_count;
753
776
  };
@@ -2791,17 +2814,59 @@ var syncSearchProvider = async (project, reporter) => {
2791
2814
 
2792
2815
  // src/cli/dev-lock.ts
2793
2816
  import {
2794
- existsSync as existsSync3,
2817
+ existsSync as existsSync5,
2795
2818
  mkdirSync,
2796
2819
  readFileSync as readFileSync2,
2797
2820
  rmSync,
2798
2821
  writeFileSync
2799
2822
  } from "node:fs";
2800
- import { join as join6 } from "pathe";
2801
- var lockPath = (outDir) => join6(outDir, "dev.lock");
2823
+ import { join as join8 } from "pathe";
2824
+
2825
+ // src/core/project.ts
2826
+ import { existsSync as existsSync4 } from "node:fs";
2827
+ import { isAbsolute, join as join7, resolve as resolve2 } from "pathe";
2828
+ var CONFIG_FILENAMES = [
2829
+ "blume.config.ts",
2830
+ "blume.config.mjs",
2831
+ "blume.config.js"
2832
+ ];
2833
+ var THEME_FILENAMES = ["theme.css"];
2834
+ var COMPONENTS_FILENAMES = ["components.tsx", "components.ts"];
2835
+ var firstExisting = (root, names) => {
2836
+ for (const name of names) {
2837
+ const candidate = join7(root, name);
2838
+ if (existsSync4(candidate)) {
2839
+ return candidate;
2840
+ }
2841
+ }
2842
+ return null;
2843
+ };
2844
+ var findConfigFile = (root) => firstExisting(root, CONFIG_FILENAMES);
2845
+ var resolveRuntimeDir = (root, runtimeDir = ".blume") => isAbsolute(runtimeDir) ? runtimeDir : join7(resolve2(root), runtimeDir);
2846
+ var resolveProjectContext = (root, config, options) => {
2847
+ const absoluteRoot = resolve2(root);
2848
+ const contentRoot = isAbsolute(config.content.root) ? config.content.root : join7(absoluteRoot, config.content.root);
2849
+ const pagesPath = join7(absoluteRoot, config.content.pages);
2850
+ const pagesRoot = existsSync4(pagesPath) ? pagesPath : null;
2851
+ const outDir = resolveRuntimeDir(absoluteRoot, options?.runtimeDir);
2852
+ const distDir = options?.runtimeDir ? join7(outDir, "dist") : join7(absoluteRoot, "dist");
2853
+ return {
2854
+ componentsFile: firstExisting(absoluteRoot, COMPONENTS_FILENAMES),
2855
+ configFile: findConfigFile(absoluteRoot),
2856
+ contentRoot,
2857
+ distDir,
2858
+ outDir,
2859
+ pagesRoot,
2860
+ root: absoluteRoot,
2861
+ themeFile: firstExisting(absoluteRoot, THEME_FILENAMES)
2862
+ };
2863
+ };
2864
+
2865
+ // src/cli/dev-lock.ts
2866
+ var lockPath = (outDir) => join8(outDir, "dev.lock");
2802
2867
  var isDevLocked = (outDir) => {
2803
2868
  const path = lockPath(outDir);
2804
- if (!existsSync3(path)) {
2869
+ if (!existsSync5(path)) {
2805
2870
  return false;
2806
2871
  }
2807
2872
  const pid = Number.parseInt(readFileSync2(path, "utf-8").trim(), 10);
@@ -2826,33 +2891,33 @@ var acquireDevLock = (outDir) => {
2826
2891
  }
2827
2892
  released = true;
2828
2893
  try {
2829
- if (existsSync3(path) && readFileSync2(path, "utf-8").trim() === String(process.pid)) {
2894
+ if (existsSync5(path) && readFileSync2(path, "utf-8").trim() === String(process.pid)) {
2830
2895
  rmSync(path, { force: true });
2831
2896
  }
2832
2897
  } catch {}
2833
2898
  };
2834
2899
  };
2835
- var refuseIfDevRunning = (root, action) => {
2836
- if (isDevLocked(join6(root, ".blume"))) {
2837
- logger.error(`A \`blume dev\` server is running against .blume; ${action} would corrupt it. Stop the dev server first.`);
2900
+ var refuseIfDevRunning = (root, action, runtimeDir) => {
2901
+ if (isDevLocked(resolveRuntimeDir(root, runtimeDir))) {
2902
+ logger.error(`A \`blume dev\` server is running against .blume; ${action} would corrupt it. Stop the dev server, or re-run with --isolated to build/verify against .blume-verify without touching it.`);
2838
2903
  process.exit(1);
2839
2904
  }
2840
2905
  };
2841
2906
 
2842
2907
  // src/astro/generate.ts
2843
- import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync } from "node:fs";
2908
+ import { existsSync as existsSync9, readFileSync as readFileSync6, realpathSync } from "node:fs";
2844
2909
  import {
2845
2910
  lstat,
2846
- mkdir as mkdir2,
2847
- readFile as readFile7,
2911
+ mkdir as mkdir3,
2912
+ readFile as readFile10,
2848
2913
  rename,
2849
2914
  rm,
2850
2915
  symlink,
2851
- writeFile as writeFile2
2916
+ writeFile as writeFile4
2852
2917
  } from "node:fs/promises";
2853
- import { createRequire as createRequire4 } from "node:module";
2918
+ import { createRequire as createRequire5 } from "node:module";
2854
2919
  import { pathToFileURL as pathToFileURL2 } from "node:url";
2855
- import { basename as basename2, dirname as dirname7, join as join13, normalize, relative as relative6 } from "pathe";
2920
+ import { basename as basename2, dirname as dirname7, join as join17, normalize as normalize3, relative as relative6 } from "pathe";
2856
2921
  import { glob as glob4 } from "tinyglobby";
2857
2922
 
2858
2923
  // src/ai/ask-data.ts
@@ -2924,7 +2989,7 @@ var askBackendRuntimeDep = (ask) => {
2924
2989
  };
2925
2990
 
2926
2991
  // src/ai/markdown.ts
2927
- import { readFile as readFile3 } from "node:fs/promises";
2992
+ import { readFile as readFile4 } from "node:fs/promises";
2928
2993
  var buildRawMarkdown = async (project) => {
2929
2994
  const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
2930
2995
  const readRoute = async (route) => {
@@ -2932,7 +2997,7 @@ var buildRawMarkdown = async (project) => {
2932
2997
  if (page) {
2933
2998
  return await readEntryText(project, page);
2934
2999
  }
2935
- return route.sourcePath ? await readFile3(route.sourcePath, "utf-8") : "";
3000
+ return route.sourcePath ? await readFile4(route.sourcePath, "utf-8") : "";
2936
3001
  };
2937
3002
  const entries = await Promise.all(project.manifest.routes.map(async (route) => [route.path, await readRoute(route)]));
2938
3003
  return Object.fromEntries(entries);
@@ -3029,6 +3094,7 @@ var buildMcpServerCard = (input) => ({
3029
3094
  var BUILTIN_MDX_TAGS = new Set([
3030
3095
  "Accordion",
3031
3096
  "AccordionItem",
3097
+ "ApiOverview",
3032
3098
  "AutoTypeTable",
3033
3099
  "Badge",
3034
3100
  "Callout",
@@ -3047,8 +3113,12 @@ var BUILTIN_MDX_TAGS = new Set([
3047
3113
  "GithubInfo",
3048
3114
  "Icon",
3049
3115
  "Math",
3116
+ "Operation",
3050
3117
  "Panel",
3118
+ "ParamField",
3051
3119
  "Prompt",
3120
+ "RequestField",
3121
+ "ResponseField",
3052
3122
  "Step",
3053
3123
  "Steps",
3054
3124
  "Tab",
@@ -3087,8 +3157,8 @@ var validateUsedComponents = (pages, extraTags, registryNames) => {
3087
3157
  };
3088
3158
 
3089
3159
  // src/core/component-overrides.ts
3090
- import { existsSync as existsSync4 } from "node:fs";
3091
- import { dirname as dirname4, extname, isAbsolute, resolve as resolve2 } from "pathe";
3160
+ import { existsSync as existsSync6 } from "node:fs";
3161
+ import { dirname as dirname4, extname, isAbsolute as isAbsolute2, resolve as resolve3 } from "pathe";
3092
3162
  import ts from "typescript";
3093
3163
  var GROUPS = ["mdx", "layout", "islands"];
3094
3164
  var FRAMEWORK_BY_EXT = {
@@ -3176,18 +3246,18 @@ var findDefaultExportObject = (sourceFile) => {
3176
3246
  var probeExtension = (base) => {
3177
3247
  for (const extension of COMPONENT_EXTS) {
3178
3248
  const candidate = `${base}.${extension}`;
3179
- if (existsSync4(candidate)) {
3249
+ if (existsSync6(candidate)) {
3180
3250
  return candidate;
3181
3251
  }
3182
3252
  }
3183
3253
  return null;
3184
3254
  };
3185
3255
  var toImport = (specifier, imported, dir) => {
3186
- const relative4 = specifier.startsWith(".") || isAbsolute(specifier);
3256
+ const relative4 = specifier.startsWith(".") || isAbsolute2(specifier);
3187
3257
  let path = specifier;
3188
3258
  let extension = extname(specifier).slice(1).toLowerCase();
3189
3259
  if (relative4) {
3190
- const absolute = isAbsolute(specifier) ? specifier : resolve2(dir, specifier);
3260
+ const absolute = isAbsolute2(specifier) ? specifier : resolve3(dir, specifier);
3191
3261
  if (extension) {
3192
3262
  path = absolute;
3193
3263
  } else {
@@ -3435,147 +3505,104 @@ var resolveUIStrings = (locale, options) => {
3435
3505
  };
3436
3506
 
3437
3507
  // src/theme/icons.ts
3438
- var icons = {
3439
- "arrow-left": '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
3440
- "arrow-right": '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
3441
- "arrow-up": '<path d="m5 12 7-7 7 7"/><path d="M12 19V5"/>',
3442
- "arrow-up-right": '<path d="M7 7h10v10"/><path d="M7 17 17 7"/>',
3443
- "badge-alert": '<path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.78 4.78 4 4 0 0 1-6.74 0 4 4 0 0 1-4.78-4.78 4 4 0 0 1 0-6.75Z"/><path d="M12 8v4"/><path d="M12 16h.01"/>',
3444
- ban: '<circle cx="12" cy="12" r="10"/><path d="m4.93 4.93 14.14 14.14"/>',
3445
- "book-open": '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>',
3446
- "book-open-cover": '<path d="M12 7v14"/><path d="M3 18a2 2 0 0 1 2-2h7V5H5a2 2 0 0 0-2 2Z"/><path d="M21 18a2 2 0 0 0-2-2h-7V5h7a2 2 0 0 1 2 2Z"/>',
3447
- "brand-x": '<path d="m4 4 11.7 16H20L8.3 4Z"/><path d="M4 20 20 4"/>',
3448
- check: '<path d="M20 6 9 17l-5-5"/>',
3449
- "chevron-down": '<path d="m6 9 6 6 6-6"/>',
3450
- "chevron-right": '<path d="m9 18 6-6-6-6"/>',
3451
- "circle-check": '<circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/>',
3452
- "circle-x": '<circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/>',
3453
- clock: '<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>',
3454
- copy: '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
3455
- download: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/>',
3456
- "external-link": '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
3457
- file: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>',
3458
- flag: '<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><path d="M4 22V15"/>',
3459
- folder: '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/>',
3460
- gear: '<path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"/><circle cx="12" cy="12" r="3"/>',
3461
- github: '<path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.3-.8 2.1-.36.16-.78.24-1.2.24-1.4 0-2.4-.7-3-2-.3-.6-.8-.9-1.2-.9-.4 0-.8.2-.8.5 0 .5.7.8 1 1.2.7 1.5 2 2.4 4 2.4.43 0 .84-.04 1.2-.13V22"/>',
3462
- globe: '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>',
3463
- info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
3464
- js: '<path d="M8 8v7a2 2 0 1 1-4 0"/><path d="M16 15a2 2 0 1 0 2-2 2 2 0 1 1 2-2"/><path d="M20 8v.01"/>',
3465
- key: '<path d="M21 2 11.4 11.6"/><circle cx="7.5" cy="16.5" r="5.5"/><path d="m15 7 2 2"/><path d="m12 10 2 2"/>',
3466
- leaf: '<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z"/><path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12"/>',
3467
- lightbulb: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/>',
3468
- link: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
3469
- linkedin: '<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-4 0v7h-4v-7a6 6 0 0 1 6-6z"/><rect width="4" height="12" x="2" y="9"/><circle cx="4" cy="4" r="2"/>',
3470
- lock: '<rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
3471
- menu: '<line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/>',
3472
- "message-circle": '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
3473
- moon: '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
3474
- "panel-left": '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/>',
3475
- "panel-left-close": '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="m16 15-3-3 3-3"/>',
3476
- "panel-right": '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/>',
3477
- "panel-right-close": '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m8 9 3 3-3 3"/>',
3478
- paperclip: '<path d="m16 6-8.41 8.41a2 2 0 0 0 2.83 2.83L18.83 8.83a4 4 0 0 0-5.66-5.66L4.76 11.59a6 6 0 0 0 8.49 8.49L21.66 11.66"/>',
3479
- "puzzle-piece": '<path d="M15.39 4.39a2.1 2.1 0 0 0-2.97 0L12 4.82l-.42-.43a2.1 2.1 0 1 0-2.97 2.97l.43.42L7.6 9.22H4.75A1.75 1.75 0 0 0 3 10.97v8.28C3 20.22 3.78 21 4.75 21h8.28c.97 0 1.75-.78 1.75-1.75V16.4l1.44-1.44.42.43a2.1 2.1 0 1 0 2.97-2.97l-.43-.42.43-.42a2.1 2.1 0 1 0-2.97-2.97l-.42.43-1.44-1.44.61-.61a2.1 2.1 0 0 0 0-2.97Z"/>',
3480
- python: '<path d="M12 2h4a4 4 0 0 1 4 4v3H8a4 4 0 0 0-4 4v1"/><path d="M12 22H8a4 4 0 0 1-4-4v-3h12a4 4 0 0 0 4-4v-1"/><path d="M9 6h.01"/><path d="M15 18h.01"/>',
3481
- rocket: '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>',
3482
- rss: '<path d="M4 11a9 9 0 0 1 9 9"/><path d="M4 4a16 16 0 0 1 16 16"/><circle cx="5" cy="19" r="1"/>',
3483
- search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
3484
- sparkles: '<path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3Z"/><path d="M5 3v4"/><path d="M3 5h4"/><path d="M19 17v4"/><path d="M17 19h4"/>',
3485
- star: '<path d="m12 2 3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01Z"/>',
3486
- sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',
3487
- "text-align-start": '<path d="M4 6h16"/><path d="M4 10h10"/><path d="M4 14h16"/><path d="M4 18h10"/>',
3488
- "thumbs-down": '<path d="M17 14V2"/><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"/>',
3489
- "thumbs-up": '<path d="M7 10v12"/><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"/>',
3490
- "triangle-alert": '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
3491
- x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>'
3492
- };
3493
- var iconAliases = {
3494
- "alien-8bit": "sparkles",
3495
- "arrow-up-right-from-square": "external-link",
3496
- "book-open-reader": "book-open",
3497
- "circle-info": "info",
3498
- close: "x",
3499
- "external-link-alt": "external-link",
3500
- "fa-github": "github",
3501
- "fa-linkedin": "linkedin",
3502
- "fa-x-twitter": "brand-x",
3503
- "file-lines": "file",
3504
- javascript: "js",
3505
- "panel-left-open": "panel-left",
3506
- "panel-right-open": "panel-right",
3507
- times: "x",
3508
- "x-twitter": "brand-x"
3509
- };
3510
- var libraryPrefixes = [
3511
- "fa-brands",
3512
- "fa-duotone",
3513
- "fa-light",
3514
- "fa-regular",
3515
- "fa-sharp-solid",
3516
- "fa-solid",
3517
- "fa-thin",
3518
- "fa",
3519
- "fab",
3520
- "fad",
3521
- "fal",
3522
- "far",
3523
- "fas",
3524
- "fat",
3525
- "lucide",
3526
- "tabler",
3527
- "ti"
3528
- ];
3529
- var normalizedIconName = (name) => name.trim().toLowerCase().replaceAll(/[\s_]+/gu, "-");
3530
- var isString = (value) => typeof value === "string";
3531
- var withoutLibraryPrefix = (name) => {
3532
- let normalized = normalizedIconName(name).replaceAll(/^icon-/gu, "");
3533
- let changed = true;
3534
- while (changed) {
3535
- changed = false;
3536
- for (const prefix of libraryPrefixes) {
3537
- if (normalized.startsWith(`${prefix}-`)) {
3538
- normalized = normalized.slice(prefix.length + 1);
3539
- changed = true;
3540
- }
3541
- if (normalized.startsWith(`${prefix}:`)) {
3542
- normalized = normalized.slice(prefix.length + 1);
3543
- changed = true;
3544
- }
3545
- }
3508
+ import { createRequire as createRequire2 } from "node:module";
3509
+ import { getIconData, iconToSVG } from "@iconify/utils";
3510
+ var requireJson = createRequire2(import.meta.url);
3511
+ var loadSet = (pkg) => requireJson(pkg);
3512
+ var SETS = {
3513
+ "fa6-brands": loadSet("@iconify-json/fa6-brands/icons.json"),
3514
+ "fa6-regular": loadSet("@iconify-json/fa6-regular/icons.json"),
3515
+ "fa6-solid": loadSet("@iconify-json/fa6-solid/icons.json"),
3516
+ lucide: loadSet("@iconify-json/lucide/icons.json"),
3517
+ tabler: loadSet("@iconify-json/tabler/icons.json")
3518
+ };
3519
+ var DEFAULT_SET = "lucide";
3520
+ var LIBRARY_SETS = {
3521
+ fa: "fa6-solid",
3522
+ "font-awesome": "fa6-solid",
3523
+ fontawesome: "fa6-solid",
3524
+ lucide: "lucide",
3525
+ tabler: "tabler"
3526
+ };
3527
+ var ICON_TYPE_SETS = {
3528
+ brands: "fa6-brands",
3529
+ duotone: "fa6-solid",
3530
+ light: "fa6-solid",
3531
+ regular: "fa6-regular",
3532
+ "sharp-solid": "fa6-solid",
3533
+ solid: "fa6-solid",
3534
+ thin: "fa6-solid"
3535
+ };
3536
+ var PREFIX_SETS = {
3537
+ ...LIBRARY_SETS,
3538
+ fa: "fa6-solid",
3539
+ "fa-brands": "fa6-brands",
3540
+ "fa-regular": "fa6-regular",
3541
+ "fa-solid": "fa6-solid",
3542
+ "fa6-brands": "fa6-brands",
3543
+ "fa6-regular": "fa6-regular",
3544
+ "fa6-solid": "fa6-solid",
3545
+ fab: "fa6-brands",
3546
+ far: "fa6-regular",
3547
+ fas: "fa6-solid",
3548
+ ti: "tabler"
3549
+ };
3550
+ var normalize = (name) => name.trim().toLowerCase().replaceAll(/[\s_]+/gu, "-");
3551
+ var setFor = (options) => {
3552
+ if (options.iconType) {
3553
+ const set = ICON_TYPE_SETS[normalize(options.iconType)];
3554
+ if (set) {
3555
+ return set;
3556
+ }
3557
+ }
3558
+ if (options.library) {
3559
+ const set = LIBRARY_SETS[normalize(options.library)];
3560
+ if (set) {
3561
+ return set;
3562
+ }
3563
+ }
3564
+ return DEFAULT_SET;
3565
+ };
3566
+ var fromSet = (setName, iconName) => {
3567
+ const set = SETS[setName];
3568
+ const data = set && getIconData(set, iconName);
3569
+ if (!data) {
3570
+ return null;
3546
3571
  }
3547
- return normalized;
3572
+ const { attributes, body } = iconToSVG(data, { height: "auto" });
3573
+ return { body, name: iconName, viewBox: attributes.viewBox };
3548
3574
  };
3549
- var resolveIcon = (name, iconType) => {
3550
- const normalized = normalizedIconName(name);
3551
- const stripped = withoutLibraryPrefix(name);
3552
- const type = iconType ? normalizedIconName(iconType) : null;
3553
- const candidates = [
3554
- normalized,
3555
- stripped,
3556
- type ? `${type}-${stripped}` : null,
3557
- iconAliases[normalized],
3558
- iconAliases[stripped]
3559
- ].filter(isString);
3560
- for (const candidate of candidates) {
3561
- const markup = icons[candidate];
3562
- if (markup) {
3563
- return { markup, name: candidate };
3575
+ var fromFaSet = (setName, name) => fromSet(setName, name) ?? fromSet("fa6-brands", name);
3576
+ var resolveInSet = (setName, name) => setName.startsWith("fa6-") ? fromFaSet(setName, name) : fromSet(setName, name);
3577
+ var resolveIcon = (name, options = {}) => {
3578
+ const normalized = normalize(name);
3579
+ const colon = normalized.indexOf(":");
3580
+ if (colon > 0) {
3581
+ const setName = PREFIX_SETS[normalized.slice(0, colon)];
3582
+ if (setName) {
3583
+ return resolveInSet(setName, normalized.slice(colon + 1));
3564
3584
  }
3565
3585
  }
3566
- return null;
3586
+ return resolveInSet(setFor(options), normalized);
3587
+ };
3588
+ var hasIcon = (name, options = {}) => {
3589
+ if (resolveIcon(name, options)) {
3590
+ return true;
3591
+ }
3592
+ const normalized = normalize(name);
3593
+ const bare = normalized.includes(":") ? normalized.slice(normalized.indexOf(":") + 1) : normalized;
3594
+ return Object.values(SETS).some((set) => getIconData(set, bare) !== null);
3567
3595
  };
3568
- var hasIcon = (name, iconType) => resolveIcon(name, iconType) !== null;
3569
3596
 
3570
3597
  // src/core/nav-diagnostics.ts
3571
3598
  var IMAGE_ICON = /^(?:https?:\/\/|data:image\/|\/|\.{1,2}\/)|\.(?:avif|gif|jpe?g|png|svg|webp)$/iu;
3572
3599
  var isAssetIcon = (value) => value.startsWith("<") || IMAGE_ICON.test(value);
3573
3600
  var flattenNodes = (nodes) => nodes.flatMap((node) => node.kind === "group" ? [node, ...flattenNodes(node.children)] : [node]);
3574
3601
  var collectIcons = (navigation) => {
3575
- const icons2 = [];
3602
+ const icons = [];
3576
3603
  const push = (icon, where) => {
3577
3604
  if (icon) {
3578
- icons2.push({ icon, where });
3605
+ icons.push({ icon, where });
3579
3606
  }
3580
3607
  };
3581
3608
  for (const tab of navigation.tabs) {
@@ -3598,7 +3625,7 @@ var collectIcons = (navigation) => {
3598
3625
  push(node.icon, `"${node.label}"`);
3599
3626
  }
3600
3627
  }
3601
- return icons2;
3628
+ return icons;
3602
3629
  };
3603
3630
  var validateNavIcons = (navigation) => {
3604
3631
  const seen = new Set;
@@ -3708,10 +3735,10 @@ var validateNavStructure = (navigation, pages) => [
3708
3735
  ];
3709
3736
 
3710
3737
  // src/core/tsconfig-aliases.ts
3711
- import { existsSync as existsSync5, readFileSync as readFileSync3, statSync } from "node:fs";
3712
- import { createRequire as createRequire2 } from "node:module";
3738
+ import { existsSync as existsSync7, readFileSync as readFileSync3, statSync } from "node:fs";
3739
+ import { createRequire as createRequire3 } from "node:module";
3713
3740
  import { pathToFileURL } from "node:url";
3714
- import { dirname as dirname5, isAbsolute as isAbsolute2, join as join7, resolve as resolve3 } from "pathe";
3741
+ import { dirname as dirname5, isAbsolute as isAbsolute3, join as join9, resolve as resolve4 } from "pathe";
3715
3742
  var stripJsonComments = (text) => {
3716
3743
  let out = "";
3717
3744
  let inString = false;
@@ -3765,16 +3792,16 @@ var isFile = (path) => {
3765
3792
  }
3766
3793
  };
3767
3794
  var resolveExtends = (spec, fromDir) => {
3768
- if (spec.startsWith(".") || isAbsolute2(spec)) {
3769
- const candidates = spec.endsWith(".json") ? [resolve3(fromDir, spec)] : [
3770
- resolve3(fromDir, `${spec}.json`),
3771
- resolve3(fromDir, spec, "tsconfig.json"),
3772
- resolve3(fromDir, spec)
3795
+ if (spec.startsWith(".") || isAbsolute3(spec)) {
3796
+ const candidates = spec.endsWith(".json") ? [resolve4(fromDir, spec)] : [
3797
+ resolve4(fromDir, `${spec}.json`),
3798
+ resolve4(fromDir, spec, "tsconfig.json"),
3799
+ resolve4(fromDir, spec)
3773
3800
  ];
3774
3801
  return candidates.find(isFile) ?? null;
3775
3802
  }
3776
3803
  try {
3777
- const require_ = createRequire2(pathToFileURL(join7(fromDir, "_.js")).href);
3804
+ const require_ = createRequire3(pathToFileURL(join9(fromDir, "_.js")).href);
3778
3805
  for (const sub of [`${spec}/tsconfig.json`, spec]) {
3779
3806
  try {
3780
3807
  return require_.resolve(sub);
@@ -3784,7 +3811,7 @@ var resolveExtends = (spec, fromDir) => {
3784
3811
  return null;
3785
3812
  };
3786
3813
  var loadPaths = (file, seen) => {
3787
- if (seen.has(file) || !existsSync5(file)) {
3814
+ if (seen.has(file) || !existsSync7(file)) {
3788
3815
  return null;
3789
3816
  }
3790
3817
  seen.add(file);
@@ -3796,7 +3823,7 @@ var loadPaths = (file, seen) => {
3796
3823
  if (options.paths && typeof options.paths === "object") {
3797
3824
  const baseUrl = typeof options.baseUrl === "string" ? options.baseUrl : ".";
3798
3825
  return {
3799
- baseDir: resolve3(dirname5(file), baseUrl),
3826
+ baseDir: resolve4(dirname5(file), baseUrl),
3800
3827
  paths: options.paths
3801
3828
  };
3802
3829
  }
@@ -3823,10 +3850,10 @@ var toAlias = (key, value, baseDir) => {
3823
3850
  if (find === "" || find === "*") {
3824
3851
  return null;
3825
3852
  }
3826
- return { find, replacement: resolve3(baseDir, target) };
3853
+ return { find, replacement: resolve4(baseDir, target) };
3827
3854
  };
3828
3855
  var resolveTsconfigAliases = (root) => {
3829
- const entry = ["tsconfig.json", "jsconfig.json"].map((name) => join7(root, name)).find((file) => existsSync5(file));
3856
+ const entry = ["tsconfig.json", "jsconfig.json"].map((name) => join9(root, name)).find((file) => existsSync7(file));
3830
3857
  if (!entry) {
3831
3858
  return {};
3832
3859
  }
@@ -3922,19 +3949,97 @@ ${items}
3922
3949
  `;
3923
3950
  };
3924
3951
 
3952
+ // src/openapi/references.ts
3953
+ var NON_SLUG = /[^a-z0-9]+/gu;
3954
+ var SLUG_EDGES = /^-+|-+$/gu;
3955
+ var ROUTE_EDGES = /^\/+|\/+$/gu;
3956
+ var TRAILING_SLASH = /\/+$/u;
3957
+ var slugify = (text) => text.toLowerCase().replace(NON_SLUG, "-").replace(SLUG_EDGES, "");
3958
+ var normalizeRoute = (route) => {
3959
+ const trimmed = route.trim();
3960
+ const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
3961
+ const noTrailing = withSlash.replace(TRAILING_SLASH, "");
3962
+ return noTrailing === "" ? "/" : noTrailing;
3963
+ };
3964
+ var routeSlug = (route) => slugify(route.replace(ROUTE_EDGES, "")) || "reference";
3965
+ var sourcesOf = (block) => {
3966
+ const sources = [...block.sources];
3967
+ if (block.spec) {
3968
+ sources.unshift({ spec: block.spec });
3969
+ }
3970
+ return sources;
3971
+ };
3972
+ var referencesFor = (kind, block, defaultLabel, renderer, display) => {
3973
+ if (!block.enabled) {
3974
+ return [];
3975
+ }
3976
+ const sources = sourcesOf(block);
3977
+ const base = normalizeRoute(block.route);
3978
+ return sources.map((source, index) => {
3979
+ const label = source.label ?? (sources.length > 1 ? `${defaultLabel} ${index + 1}` : defaultLabel);
3980
+ let route;
3981
+ if (source.route) {
3982
+ route = normalizeRoute(source.route);
3983
+ } else if (sources.length === 1) {
3984
+ route = base;
3985
+ } else {
3986
+ const suffix = source.label ? slugify(source.label) : "";
3987
+ route = normalizeRoute(`${base}/${suffix || index + 1}`);
3988
+ }
3989
+ return {
3990
+ display,
3991
+ kind,
3992
+ label,
3993
+ renderer,
3994
+ route,
3995
+ slug: routeSlug(route),
3996
+ spec: source.spec,
3997
+ theme: block.theme
3998
+ };
3999
+ });
4000
+ };
4001
+ var NO_DISPLAY = { codeSamples: [], expandSchemas: false };
4002
+ var resolveReferences = (config) => [
4003
+ ...referencesFor("openapi", config.openapi, "API Reference", config.openapi.renderer, {
4004
+ codeSamples: config.openapi.codeSamples,
4005
+ expandSchemas: config.openapi.expandSchemas
4006
+ }),
4007
+ ...referencesFor("asyncapi", config.asyncapi, "Events", "scalar", NO_DISPLAY)
4008
+ ];
4009
+ var referenceTabs = (config) => resolveReferences(config).map((ref) => ({
4010
+ label: ref.label,
4011
+ path: ref.route
4012
+ }));
4013
+ var blumeReferences = (config) => {
4014
+ const seen = new Set;
4015
+ const result = [];
4016
+ for (const ref of resolveReferences(config)) {
4017
+ if (ref.kind !== "openapi" || ref.renderer !== "blume") {
4018
+ continue;
4019
+ }
4020
+ if (seen.has(ref.route)) {
4021
+ continue;
4022
+ }
4023
+ seen.add(ref.route);
4024
+ result.push(ref);
4025
+ }
4026
+ return result;
4027
+ };
4028
+ var hasScalarReferences = (config) => resolveReferences(config).some((ref) => ref.renderer === "scalar");
4029
+
3925
4030
  // src/openapi/scalar.ts
3926
- import { readFile as readFile4 } from "node:fs/promises";
3927
- import { isAbsolute as isAbsolute3, join as join10 } from "pathe";
4031
+ import { readFile as readFile5 } from "node:fs/promises";
4032
+ import { isAbsolute as isAbsolute4, join as join12 } from "pathe";
3928
4033
 
3929
4034
  // src/astro/templates.ts
3930
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
3931
- import { dirname as dirname6, join as join9 } from "pathe";
4035
+ import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
4036
+ import { dirname as dirname6, join as join11 } from "pathe";
3932
4037
 
3933
4038
  // src/core/assets.ts
3934
- import { join as join8 } from "pathe";
4039
+ import { join as join10 } from "pathe";
3935
4040
  var resolveAssetMounts = (root, assets) => assets.map((entry) => {
3936
4041
  const rel = entry.replace(/^[./]+/u, "").replaceAll(/\.\.\/?/gu, "").replace(/\/+$/u, "");
3937
- return { dir: join8(root, rel), url: `/${rel}` };
4042
+ return { dir: join10(root, rel), url: `/${rel}` };
3938
4043
  });
3939
4044
 
3940
4045
  // src/theme/fonts.ts
@@ -4093,7 +4198,7 @@ var WORKSPACE_MARKERS = [
4093
4198
  "yarn.lock"
4094
4199
  ];
4095
4200
  var hasWorkspacesField = (pkgPath) => {
4096
- if (!existsSync6(pkgPath)) {
4201
+ if (!existsSync8(pkgPath)) {
4097
4202
  return false;
4098
4203
  }
4099
4204
  try {
@@ -4103,7 +4208,7 @@ var hasWorkspacesField = (pkgPath) => {
4103
4208
  return false;
4104
4209
  }
4105
4210
  };
4106
- var hasWorkspaceMarker = (dir) => hasWorkspacesField(join9(dir, "package.json")) || WORKSPACE_MARKERS.some((marker) => existsSync6(join9(dir, marker)));
4211
+ var hasWorkspaceMarker = (dir) => hasWorkspacesField(join11(dir, "package.json")) || WORKSPACE_MARKERS.some((marker) => existsSync8(join11(dir, marker)));
4107
4212
  var findWorkspaceRoot = (start) => {
4108
4213
  let dir = start;
4109
4214
  for (;; ) {
@@ -4138,7 +4243,7 @@ var runtimeDependencies = (options) => {
4138
4243
  if (needsSvelte) {
4139
4244
  deps.push("@astrojs/svelte");
4140
4245
  }
4141
- if (config.openapi.enabled || config.asyncapi.enabled) {
4246
+ if (hasScalarReferences(config)) {
4142
4247
  deps.push("@scalar/astro");
4143
4248
  }
4144
4249
  deps.push(...searchProviderMeta(config.search.provider).runtimeDeps);
@@ -4171,6 +4276,7 @@ var RENDER_EXTERNAL_DEPS = [
4171
4276
  ];
4172
4277
  var renderUserAliases = (aliases) => Object.entries(aliases ?? {}).toSorted(([a], [b]) => b.length - a.length).map(([find, replacement]) => `
4173
4278
  ${JSON.stringify(find)}: ${JSON.stringify(replacement)},`).join("");
4279
+ var astroOutDir = (context) => context.distDir ?? `${context.root}/dist`;
4174
4280
  var astroConfigTemplate = (options) => {
4175
4281
  const { context, config, needsReact, pages, dataPath, themePath } = options;
4176
4282
  const {
@@ -4178,6 +4284,7 @@ var astroConfigTemplate = (options) => {
4178
4284
  examplesPath,
4179
4285
  needsSvelte,
4180
4286
  needsVue,
4287
+ openapiPath,
4181
4288
  searchClientPath
4182
4289
  } = options;
4183
4290
  const { deployment } = config;
@@ -4248,7 +4355,7 @@ ${twoslashImport}${reactImport}${vueImport}${svelteImport}${blumeImport}${adapte
4248
4355
  export default defineConfig({
4249
4356
  root: ${JSON.stringify(context.outDir)},
4250
4357
  srcDir: ${JSON.stringify(`${context.outDir}/src`)},
4251
- outDir: ${JSON.stringify(`${context.root}/dist`)},
4358
+ outDir: ${JSON.stringify(astroOutDir(context))},
4252
4359
  publicDir: ${JSON.stringify(`${context.root}/public`)},
4253
4360
  output: ${JSON.stringify(deployment.output)},${adapterOption}${siteOption}${baseOption}${redirectsOption}${i18nOption}${fontsOption}
4254
4361
  integrations: [${integrations.join(", ")}],
@@ -4281,6 +4388,7 @@ export default defineConfig({
4281
4388
  alias: {
4282
4389
  "blume:data": ${JSON.stringify(dataPath)},
4283
4390
  "blume:examples": ${JSON.stringify(examplesPath)},
4391
+ "blume:openapi": ${JSON.stringify(openapiPath)},
4284
4392
  "blume:search-client": ${JSON.stringify(searchClientPath)},
4285
4393
  "blume:theme": ${JSON.stringify(themePath)},${userAliasLines}
4286
4394
  },
@@ -4294,7 +4402,7 @@ export default defineConfig({
4294
4402
  });
4295
4403
  `;
4296
4404
  };
4297
- var stagedContentDir = (outDir) => join9(outDir, "content");
4405
+ var stagedContentDir = (outDir) => join11(outDir, "content");
4298
4406
  var contentConfigTemplate = (options) => {
4299
4407
  const { context, config } = options;
4300
4408
  const stagedBase = options.stagedBase ?? stagedContentDir(context.outDir);
@@ -4700,7 +4808,10 @@ import FileTree from "blume/components/content/FileTree.astro";
4700
4808
  import Frame from "blume/components/content/Frame.astro";
4701
4809
  import GithubInfo from "blume/components/content/GithubInfo.astro";
4702
4810
  import Panel from "blume/components/content/Panel.astro";
4811
+ import ParamField from "blume/components/content/ParamField.astro";
4703
4812
  import Prompt from "blume/components/content/Prompt.astro";
4813
+ import RequestField from "blume/components/content/RequestField.astro";
4814
+ import ResponseField from "blume/components/content/ResponseField.astro";
4704
4815
  import Step from "blume/components/content/Step.astro";
4705
4816
  import Steps from "blume/components/content/Steps.astro";
4706
4817
  import Tab from "blume/components/content/Tab.astro";
@@ -4714,6 +4825,8 @@ import TypeTable from "blume/components/content/TypeTable.astro";
4714
4825
  import Visibility from "blume/components/content/Visibility.astro";
4715
4826
  import YouTube from "blume/components/content/YouTube.astro";
4716
4827
  import Icon from "blume/components/Icon.astro";
4828
+ import ApiOverview from "blume/components/openapi/ApiOverview.astro";
4829
+ import Operation from "blume/components/openapi/Operation.astro";
4717
4830
  ${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../generated/components.ts";
4718
4831
  import { islandComponents } from "../generated/islands.ts";
4719
4832
  import data from "../generated/data.json";
@@ -4730,6 +4843,7 @@ export const prerender = true;
4730
4843
  const components = {
4731
4844
  Accordion,
4732
4845
  AccordionItem,
4846
+ ApiOverview,
4733
4847
  AutoTypeTable,
4734
4848
  Badge,
4735
4849
  Callout,
@@ -4747,8 +4861,12 @@ const components = {
4747
4861
  Frame,
4748
4862
  GithubInfo,
4749
4863
  Icon,
4864
+ Operation,
4750
4865
  Panel,
4866
+ ParamField,
4751
4867
  Prompt,
4868
+ RequestField,
4869
+ ResponseField,
4752
4870
  Step,
4753
4871
  Steps,
4754
4872
  Tab,
@@ -5148,6 +5266,11 @@ declare module "blume:data" {
5148
5266
  export default data;
5149
5267
  }
5150
5268
 
5269
+ declare module "blume:openapi" {
5270
+ const specs: import("blume/openapi/model.ts").OpenApiData;
5271
+ export default specs;
5272
+ }
5273
+
5151
5274
  declare module "blume:search-client" {
5152
5275
  export const createSearch: () =>
5153
5276
  | import("blume/components/layout/search/types.ts").SearchFn
@@ -5262,57 +5385,11 @@ ${dark}`;
5262
5385
 
5263
5386
  // src/openapi/scalar.ts
5264
5387
  var URL_SPEC = /^https?:\/\//u;
5265
- var NON_SLUG = /[^a-z0-9]+/gu;
5266
- var SLUG_EDGES = /^-+|-+$/gu;
5267
- var ROUTE_EDGES = /^\/+|\/+$/gu;
5268
- var TRAILING_SLASH = /\/+$/u;
5269
- var slugify = (text) => text.toLowerCase().replace(NON_SLUG, "-").replace(SLUG_EDGES, "");
5270
- var normalizeRoute = (route) => {
5271
- const trimmed = route.trim();
5272
- const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
5273
- const noTrailing = withSlash.replace(TRAILING_SLASH, "");
5274
- return noTrailing === "" ? "/" : noTrailing;
5275
- };
5388
+ var ROUTE_EDGES2 = /^\/+|\/+$/gu;
5276
5389
  var referencePagePath = (route) => {
5277
- const segments = route.replace(ROUTE_EDGES, "");
5390
+ const segments = route.replace(ROUTE_EDGES2, "");
5278
5391
  return `${segments === "" ? "index" : segments}.astro`;
5279
5392
  };
5280
- var sourcesOf = (block) => {
5281
- const sources = [...block.sources];
5282
- if (block.spec) {
5283
- sources.unshift({ spec: block.spec });
5284
- }
5285
- return sources;
5286
- };
5287
- var referencesFor = (kind, block, defaultLabel) => {
5288
- if (!block.enabled) {
5289
- return [];
5290
- }
5291
- const sources = sourcesOf(block);
5292
- const base = normalizeRoute(block.route);
5293
- return sources.map((source, index) => {
5294
- const label = source.label ?? (sources.length > 1 ? `${defaultLabel} ${index + 1}` : defaultLabel);
5295
- let route;
5296
- if (source.route) {
5297
- route = normalizeRoute(source.route);
5298
- } else if (sources.length === 1) {
5299
- route = base;
5300
- } else {
5301
- const suffix = source.label ? slugify(source.label) : "";
5302
- route = normalizeRoute(`${base}/${suffix || index + 1}`);
5303
- }
5304
- return { kind, label, route, spec: source.spec, theme: block.theme };
5305
- });
5306
- };
5307
- var resolveReferences = (config) => [
5308
- ...referencesFor("openapi", config.openapi, "API Reference"),
5309
- ...referencesFor("asyncapi", config.asyncapi, "Events")
5310
- ];
5311
- var referenceTabs = (config) => resolveReferences(config).map((ref) => ({
5312
- label: ref.label,
5313
- path: ref.route
5314
- }));
5315
- var hasReferences = (config) => config.openapi.enabled || config.asyncapi.enabled;
5316
5393
  var darkModeConfig = (mode) => {
5317
5394
  if (mode === "dark") {
5318
5395
  return { darkMode: true };
@@ -5337,9 +5414,9 @@ var specConfiguration = async (spec, root) => {
5337
5414
  if (URL_SPEC.test(spec)) {
5338
5415
  return { config: { url: spec } };
5339
5416
  }
5340
- const absolute = isAbsolute3(spec) ? spec : join10(root, spec);
5417
+ const absolute = isAbsolute4(spec) ? spec : join12(root, spec);
5341
5418
  try {
5342
- return { config: { content: await readFile4(absolute, "utf-8") } };
5419
+ return { config: { content: await readFile5(absolute, "utf-8") } };
5343
5420
  } catch {
5344
5421
  return {
5345
5422
  config: { url: spec },
@@ -5353,6 +5430,9 @@ var buildReferenceFiles = async (options) => {
5353
5430
  const seen = new Set;
5354
5431
  const accepted = [];
5355
5432
  for (const ref of resolveReferences(config)) {
5433
+ if (ref.renderer !== "scalar") {
5434
+ continue;
5435
+ }
5356
5436
  if (seen.has(ref.route)) {
5357
5437
  warnings.push(`Two API reference sources resolve to ${ref.route}; keeping the first.`);
5358
5438
  continue;
@@ -5373,22 +5453,300 @@ var buildReferenceFiles = async (options) => {
5373
5453
  if (spec.warning) {
5374
5454
  warnings.push(spec.warning);
5375
5455
  }
5376
- const pagePath = referencePagePath(ref.route);
5377
- const depth = pagePath.split("/").length - 1;
5378
- files.push({
5379
- content: scalarReferenceTemplate({
5380
- configuration: {
5381
- ...spec.config,
5382
- ...themeConfiguration(config, ref.theme)
5383
- },
5384
- dataImport: `${"../".repeat(depth + 1)}generated/data.json`,
5385
- route: ref.route,
5386
- title: ref.label
5387
- }),
5388
- pagePath
5389
- });
5390
- }
5391
- return { files, warnings };
5456
+ const pagePath = referencePagePath(ref.route);
5457
+ const depth = pagePath.split("/").length - 1;
5458
+ files.push({
5459
+ content: scalarReferenceTemplate({
5460
+ configuration: {
5461
+ ...spec.config,
5462
+ ...themeConfiguration(config, ref.theme)
5463
+ },
5464
+ dataImport: `${"../".repeat(depth + 1)}generated/data.json`,
5465
+ route: ref.route,
5466
+ title: ref.label
5467
+ }),
5468
+ pagePath
5469
+ });
5470
+ }
5471
+ return { files, warnings };
5472
+ };
5473
+
5474
+ // src/core/sources/cache.ts
5475
+ import { mkdir as mkdir2, readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
5476
+ import { join as join13 } from "pathe";
5477
+ var hashText = (text) => {
5478
+ let hash = 5381;
5479
+ for (let i = 0;i < text.length; i += 1) {
5480
+ hash = (hash * 33 + (text.codePointAt(i) ?? 0)) % 2147483647;
5481
+ }
5482
+ return hash.toString(36);
5483
+ };
5484
+ var entriesDigest = (entries) => hashText(entries.map((entry) => `${entry.ref}:${entry.hash ?? hashText(entry.body.text)}`).join("|"));
5485
+ var pollingWatch = (load2, intervalSeconds) => (onChange) => {
5486
+ let last = "";
5487
+ const tick = async () => {
5488
+ try {
5489
+ const { entries } = await load2();
5490
+ const next = entriesDigest(entries);
5491
+ if (last && next !== last) {
5492
+ onChange();
5493
+ }
5494
+ last = next;
5495
+ } catch {}
5496
+ };
5497
+ const timer = setInterval(() => {
5498
+ tick();
5499
+ }, intervalSeconds * 1000);
5500
+ return () => clearInterval(timer);
5501
+ };
5502
+ var snapshotCache = (cacheDir) => {
5503
+ const file = join13(cacheDir, "entries.json");
5504
+ return {
5505
+ read: async () => {
5506
+ try {
5507
+ return JSON.parse(await readFile6(file, "utf-8"));
5508
+ } catch {
5509
+ return [];
5510
+ }
5511
+ },
5512
+ write: async (entries) => {
5513
+ try {
5514
+ await mkdir2(cacheDir, { recursive: true });
5515
+ await writeFile3(file, `${JSON.stringify(entries)}
5516
+ `, "utf-8");
5517
+ } catch {}
5518
+ }
5519
+ };
5520
+ };
5521
+ var loadWithCache = async (name, cache, fetchEntries, refresh = true) => {
5522
+ if (!refresh) {
5523
+ const cached3 = await cache.read();
5524
+ if (cached3.length > 0) {
5525
+ return { diagnostics: [], entries: cached3 };
5526
+ }
5527
+ }
5528
+ try {
5529
+ const entries = await fetchEntries();
5530
+ await cache.write(entries);
5531
+ return { diagnostics: [], entries };
5532
+ } catch (error) {
5533
+ const fallback = await cache.read();
5534
+ if (fallback.length > 0) {
5535
+ const diagnostic = {
5536
+ code: "BLUME_SOURCE_OFFLINE",
5537
+ message: `Source "${name}" could not be fetched (${error.message}); served ${fallback.length} cached entries.`,
5538
+ severity: "warning"
5539
+ };
5540
+ return { diagnostics: [diagnostic], entries: fallback };
5541
+ }
5542
+ throw new BlumeError({
5543
+ code: "BLUME_SOURCE_FETCH_FAILED",
5544
+ message: `Source "${name}" failed to load and no cache is available: ${error.message}`,
5545
+ severity: "error"
5546
+ });
5547
+ }
5548
+ };
5549
+
5550
+ // src/openapi/model.ts
5551
+ var NON_SLUG2 = /[^a-z0-9]+/gu;
5552
+ var SLUG_EDGES2 = /^-+|-+$/gu;
5553
+ var slugify2 = (text) => text.toLowerCase().replace(NON_SLUG2, "-").replace(SLUG_EDGES2, "");
5554
+ var HTTP_METHODS = [
5555
+ "get",
5556
+ "put",
5557
+ "post",
5558
+ "delete",
5559
+ "options",
5560
+ "head",
5561
+ "patch",
5562
+ "trace"
5563
+ ];
5564
+ var UNTAGGED = "Operations";
5565
+ var operationKey = (method, path, operationId) => {
5566
+ const fromId = operationId ? slugify2(operationId) : "";
5567
+ return fromId || slugify2(`${method}-${path}`);
5568
+ };
5569
+ var isOperation = (value) => typeof value === "object" && value !== null;
5570
+ var extractOperations = (document, baseRoute) => {
5571
+ const operations = [];
5572
+ const tagOrder = [];
5573
+ const tagMeta = new Map((document.tags ?? []).map((tag) => [tag.name, tag.description ?? ""]));
5574
+ const seen = new Set;
5575
+ for (const [path, rawItem] of Object.entries(document.paths ?? {})) {
5576
+ const item = rawItem;
5577
+ if (!item || "$ref" in item) {
5578
+ continue;
5579
+ }
5580
+ for (const method of HTTP_METHODS) {
5581
+ const operation = item[method];
5582
+ if (!isOperation(operation)) {
5583
+ continue;
5584
+ }
5585
+ const tag = operation.tags?.[0] ?? UNTAGGED;
5586
+ const tagSlug = slugify2(tag) || "operations";
5587
+ if (!tagOrder.includes(tag)) {
5588
+ tagOrder.push(tag);
5589
+ }
5590
+ let key = operationKey(method, path, operation.operationId);
5591
+ while (seen.has(key)) {
5592
+ key = `${key}-${method}`;
5593
+ }
5594
+ seen.add(key);
5595
+ operations.push({
5596
+ deprecated: operation.deprecated ?? false,
5597
+ description: operation.description ?? "",
5598
+ key,
5599
+ method,
5600
+ operationId: operation.operationId,
5601
+ path,
5602
+ route: `${baseRoute}/${tagSlug}/${key}`,
5603
+ summary: operation.summary ?? "",
5604
+ tag,
5605
+ tagSlug
5606
+ });
5607
+ }
5608
+ }
5609
+ const tags = tagOrder.map((name) => ({
5610
+ description: tagMeta.get(name) ?? "",
5611
+ name,
5612
+ slug: slugify2(name) || "operations"
5613
+ }));
5614
+ return { operations, tags };
5615
+ };
5616
+
5617
+ // src/openapi/parse.ts
5618
+ import { readFile as readFile7 } from "node:fs/promises";
5619
+ import { normalize as normalize2, upgrade } from "@scalar/openapi-parser";
5620
+ import { isAbsolute as isAbsolute5, join as join14 } from "pathe";
5621
+ var URL_SPEC2 = /^https?:\/\//u;
5622
+ var readSpecText = async (spec, root) => {
5623
+ if (URL_SPEC2.test(spec)) {
5624
+ const response = await fetch(spec);
5625
+ if (!response.ok) {
5626
+ throw new Error(`${spec} -> ${response.status} ${response.statusText}`);
5627
+ }
5628
+ return await response.text();
5629
+ }
5630
+ const absolute = isAbsolute5(spec) ? spec : join14(root, spec);
5631
+ return await readFile7(absolute, "utf-8");
5632
+ };
5633
+ var parseSpec = async (spec, root) => {
5634
+ const text = await readSpecText(spec, root);
5635
+ const normalized = normalize2(text);
5636
+ const { specification } = upgrade(normalized);
5637
+ return { document: specification, warnings: [] };
5638
+ };
5639
+
5640
+ // src/openapi/render-mdx.ts
5641
+ var MDX_UNSAFE = /[<>{}]/gu;
5642
+ var ENTITIES = {
5643
+ "<": "&lt;",
5644
+ ">": "&gt;",
5645
+ "{": "&#123;",
5646
+ "}": "&#125;"
5647
+ };
5648
+ var mdxSafe = (text) => text.replace(MDX_UNSAFE, (char) => ENTITIES[char] ?? char);
5649
+ var withDescription = (description, component) => description.trim() ? `${mdxSafe(description.trim())}
5650
+
5651
+ ${component}` : component;
5652
+ var operationMdx = (spec, operation) => {
5653
+ const method = operation.method.toUpperCase();
5654
+ const title = operation.summary || `${method} ${operation.path}`;
5655
+ const description = operation.description.trim() === operation.summary.trim() ? "" : operation.description;
5656
+ return {
5657
+ body: withDescription(description, `<Operation source="${spec.slug}" id="${operation.key}" />`),
5658
+ data: {
5659
+ ...operation.deprecated ? { deprecated: true } : {},
5660
+ search: { tags: [operation.tag, method] },
5661
+ sidebar: { badge: method, label: operation.summary || operation.path },
5662
+ title,
5663
+ type: "openapi-operation"
5664
+ }
5665
+ };
5666
+ };
5667
+ var overviewMdx = (spec) => ({
5668
+ body: withDescription(spec.description, `<ApiOverview source="${spec.slug}" />`),
5669
+ data: {
5670
+ sidebar: { label: "Overview" },
5671
+ title: spec.title || spec.label
5672
+ }
5673
+ });
5674
+
5675
+ // src/openapi/source.ts
5676
+ var isOpenApiSource = (source) => source.kind === "openapi-source";
5677
+ var routeToRef = (route) => route.replace(/^\/+/u, "");
5678
+ var toEntry = (rendered, ref) => {
5679
+ const raw = frontmatter_default.stringify(`${rendered.body}
5680
+ `, rendered.data);
5681
+ return {
5682
+ body: { format: "mdx", text: rendered.body },
5683
+ data: rendered.data,
5684
+ hash: hashText(raw),
5685
+ raw,
5686
+ ref
5687
+ };
5688
+ };
5689
+ var specEntries = (spec, operations) => {
5690
+ const entries = operations.map((operation) => toEntry(operationMdx(spec, operation), `${routeToRef(operation.route)}.mdx`));
5691
+ entries.push(toEntry(overviewMdx(spec), `${routeToRef(spec.route)}/index.mdx`));
5692
+ return entries;
5693
+ };
5694
+ var openApiSource = (references, ctx) => {
5695
+ let parsed = {};
5696
+ const loadReference = async (reference) => {
5697
+ try {
5698
+ const { document } = await parseSpec(reference.spec, ctx.projectRoot);
5699
+ const { operations, tags } = extractOperations(document, reference.route);
5700
+ const info = document.info ?? { title: reference.label, version: "" };
5701
+ const spec = {
5702
+ codeSamples: reference.display.codeSamples,
5703
+ description: info.description ?? "",
5704
+ document,
5705
+ expandSchemas: reference.display.expandSchemas,
5706
+ label: reference.label,
5707
+ operations: Object.fromEntries(operations.map((operation) => [operation.key, operation])),
5708
+ route: reference.route,
5709
+ slug: reference.slug,
5710
+ tags,
5711
+ title: info.title ?? reference.label,
5712
+ version: info.version ?? ""
5713
+ };
5714
+ return {
5715
+ entries: specEntries(spec, operations),
5716
+ slug: reference.slug,
5717
+ spec
5718
+ };
5719
+ } catch (error) {
5720
+ return {
5721
+ code: "BLUME_OPENAPI_UNAVAILABLE",
5722
+ message: `Could not load OpenAPI spec "${reference.spec}" for ${reference.route} (${error.message}); its reference pages were skipped.`,
5723
+ severity: "warning"
5724
+ };
5725
+ }
5726
+ };
5727
+ const load2 = async () => {
5728
+ const results = await Promise.all(references.map(loadReference));
5729
+ const entries = [];
5730
+ const diagnostics = [];
5731
+ const data = {};
5732
+ for (const result of results) {
5733
+ if ("severity" in result) {
5734
+ diagnostics.push(result);
5735
+ continue;
5736
+ }
5737
+ data[result.slug] = result.spec;
5738
+ entries.push(...result.entries);
5739
+ }
5740
+ parsed = data;
5741
+ return { diagnostics, entries };
5742
+ };
5743
+ return {
5744
+ kind: "openapi-source",
5745
+ load: load2,
5746
+ name: "openapi",
5747
+ openApiData: () => parsed,
5748
+ staged: true
5749
+ };
5392
5750
  };
5393
5751
 
5394
5752
  // src/theme/entry.ts
@@ -6011,8 +6369,8 @@ ${options.userTheme}
6011
6369
 
6012
6370
  // src/theme/twoslash.ts
6013
6371
  import { readFileSync as readFileSync5 } from "node:fs";
6014
- import { createRequire as createRequire3 } from "node:module";
6015
- var require2 = createRequire3(import.meta.url);
6372
+ import { createRequire as createRequire4 } from "node:module";
6373
+ var require2 = createRequire4(import.meta.url);
6016
6374
  var OVERRIDES = `
6017
6375
  /* Twoslash: theme the rich renderer with Blume tokens. */
6018
6376
  :root {
@@ -6174,13 +6532,13 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
6174
6532
  };
6175
6533
 
6176
6534
  // src/astro/examples.ts
6177
- import { readFile as readFile6 } from "node:fs/promises";
6178
- import { join as join12, relative as relative4 } from "pathe";
6535
+ import { readFile as readFile9 } from "node:fs/promises";
6536
+ import { join as join16, relative as relative4 } from "pathe";
6179
6537
  import { glob as glob2 } from "tinyglobby";
6180
6538
 
6181
6539
  // src/astro/islands.ts
6182
- import { readFile as readFile5 } from "node:fs/promises";
6183
- import { basename, join as join11 } from "pathe";
6540
+ import { readFile as readFile8 } from "node:fs/promises";
6541
+ import { basename, join as join15 } from "pathe";
6184
6542
  import { glob } from "tinyglobby";
6185
6543
  var DEFAULT_CLIENT = "visible";
6186
6544
  var VALID_MODES = new Set([
@@ -6209,14 +6567,14 @@ var readClientMode = (source, file, warnings) => {
6209
6567
  return mode;
6210
6568
  };
6211
6569
  var discoverIslands = async (root) => {
6212
- const dir = join11(root, "islands");
6570
+ const dir = join15(root, "islands");
6213
6571
  const matches = await glob(["**/*.{jsx,svelte,tsx,vue}"], {
6214
6572
  absolute: true,
6215
6573
  cwd: dir,
6216
6574
  onlyFiles: true
6217
6575
  });
6218
6576
  const files = matches.toSorted();
6219
- const sources = await Promise.all(files.map((file) => readFile5(file, "utf-8")));
6577
+ const sources = await Promise.all(files.map((file) => readFile8(file, "utf-8")));
6220
6578
  const islands = [];
6221
6579
  const warnings = [];
6222
6580
  const seen = new Map;
@@ -6272,14 +6630,14 @@ var splitGlobBase = (pattern) => {
6272
6630
  };
6273
6631
  var discoverExamples = async (root, pattern = "examples") => {
6274
6632
  const { base, rest } = GLOB_MAGIC.test(pattern) ? splitGlobBase(pattern) : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };
6275
- const dir = join12(root, base);
6633
+ const dir = join16(root, base);
6276
6634
  const matches = await glob2([rest], {
6277
6635
  absolute: true,
6278
6636
  cwd: dir,
6279
6637
  onlyFiles: true
6280
6638
  });
6281
6639
  const files = matches.toSorted();
6282
- const sources = await Promise.all(files.map((file) => readFile6(file, "utf-8")));
6640
+ const sources = await Promise.all(files.map((file) => readFile9(file, "utf-8")));
6283
6641
  const examples = [];
6284
6642
  const warnings = [];
6285
6643
  const seen = new Map;
@@ -6353,10 +6711,10 @@ var customOgRoutes = (pages, siteTitle) => {
6353
6711
  };
6354
6712
 
6355
6713
  // src/astro/generate.ts
6356
- var BLUME_SRC = join13(packageRoot(), "src");
6714
+ var BLUME_SRC = join17(packageRoot(), "src");
6357
6715
  var canResolveFrom = (fromDir, spec) => {
6358
6716
  try {
6359
- createRequire4(pathToFileURL2(join13(fromDir, "_.js")).href).resolve(spec);
6717
+ createRequire5(pathToFileURL2(join17(fromDir, "_.js")).href).resolve(spec);
6360
6718
  return true;
6361
6719
  } catch {
6362
6720
  return false;
@@ -6364,15 +6722,15 @@ var canResolveFrom = (fromDir, spec) => {
6364
6722
  };
6365
6723
  var resolvedAstroPath = (fromDir) => {
6366
6724
  try {
6367
- const pkg = createRequire4(pathToFileURL2(join13(fromDir, "_.js")).href).resolve("astro/package.json");
6725
+ const pkg = createRequire5(pathToFileURL2(join17(fromDir, "_.js")).href).resolve("astro/package.json");
6368
6726
  return realpathSync(pkg);
6369
6727
  } catch {
6370
6728
  return null;
6371
6729
  }
6372
6730
  };
6373
6731
  var blumeDepsDir = (pkgDir = packageRoot()) => {
6374
- const candidates = [join13(pkgDir, "node_modules"), dirname7(pkgDir)];
6375
- return candidates.find((dir) => existsSync7(join13(dir, "astro"))) ?? null;
6732
+ const candidates = [join17(pkgDir, "node_modules"), dirname7(pkgDir)];
6733
+ return candidates.find((dir) => existsSync9(join17(dir, "astro"))) ?? null;
6376
6734
  };
6377
6735
  var linkDepsJunction = async (link, depsDir) => {
6378
6736
  const existing = await lstat(link).catch(() => null);
@@ -6382,7 +6740,7 @@ var linkDepsJunction = async (link, depsDir) => {
6382
6740
  }
6383
6741
  await rm(link, { force: true });
6384
6742
  }
6385
- await mkdir2(dirname7(link), { recursive: true });
6743
+ await mkdir3(dirname7(link), { recursive: true });
6386
6744
  await symlink(depsDir, link, "junction");
6387
6745
  };
6388
6746
  var readPkgVersion = (pkgJsonPath) => {
@@ -6412,8 +6770,8 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
6412
6770
  if (blumeAstro && outDirAstro === blumeAstro) {
6413
6771
  return null;
6414
6772
  }
6415
- if (existsSync7(join13(depsDir, "@astrojs", "mdx"))) {
6416
- await linkDepsJunction(join13(outDir, "node_modules"), depsDir);
6773
+ if (existsSync9(join17(depsDir, "@astrojs", "mdx"))) {
6774
+ await linkDepsJunction(join17(outDir, "node_modules"), depsDir);
6417
6775
  return null;
6418
6776
  }
6419
6777
  return astroConflictWarning(blumeAstro, outDirAstro);
@@ -6437,7 +6795,7 @@ var readOptional = async (path) => {
6437
6795
  return "";
6438
6796
  }
6439
6797
  try {
6440
- return await readFile7(path, "utf-8");
6798
+ return await readFile10(path, "utf-8");
6441
6799
  } catch {
6442
6800
  return "";
6443
6801
  }
@@ -6453,16 +6811,16 @@ var detectNeedsReact = async (root) => {
6453
6811
  var writeIfChanged = async (path, content) => {
6454
6812
  let existing = null;
6455
6813
  try {
6456
- existing = await readFile7(path, "utf-8");
6814
+ existing = await readFile10(path, "utf-8");
6457
6815
  } catch {
6458
6816
  existing = null;
6459
6817
  }
6460
6818
  if (existing === content) {
6461
6819
  return false;
6462
6820
  }
6463
- await mkdir2(dirname7(path), { recursive: true });
6821
+ await mkdir3(dirname7(path), { recursive: true });
6464
6822
  const tmp = `${path}.${process.pid}.tmp`;
6465
- await writeFile2(tmp, content, "utf-8");
6823
+ await writeFile4(tmp, content, "utf-8");
6466
6824
  try {
6467
6825
  await rename(tmp, path);
6468
6826
  } catch (error) {
@@ -6477,7 +6835,7 @@ var pruneOrphans = async (srcDir, written) => {
6477
6835
  cwd: srcDir,
6478
6836
  onlyFiles: true
6479
6837
  });
6480
- await Promise.all(existing.map((path) => normalize(path)).filter((path) => !written.has(path)).map((path) => rm(path, { force: true })));
6838
+ await Promise.all(existing.map((path) => normalize3(path)).filter((path) => !written.has(path)).map((path) => rm(path, { force: true })));
6481
6839
  };
6482
6840
  var collectStaged = (project) => {
6483
6841
  const staged = new Map;
@@ -6492,11 +6850,11 @@ var writeStagedContent = async (out, staged) => {
6492
6850
  const contentDir = stagedContentDir(out);
6493
6851
  const written = new Set;
6494
6852
  await Promise.all([...staged].map(async ([entryId, text]) => {
6495
- const path = join13(contentDir, entryId);
6496
- written.add(normalize(path));
6853
+ const path = join17(contentDir, entryId);
6854
+ written.add(normalize3(path));
6497
6855
  await writeIfChanged(path, text);
6498
6856
  }));
6499
- if (existsSync7(contentDir)) {
6857
+ if (existsSync9(contentDir)) {
6500
6858
  await pruneOrphans(contentDir, written);
6501
6859
  }
6502
6860
  };
@@ -6513,9 +6871,9 @@ var resolveLogo = (project) => {
6513
6871
  if (light && light === dark && light.toLowerCase().endsWith(".svg")) {
6514
6872
  const rel = light.replace(/^\//u, "");
6515
6873
  const file = [
6516
- join13(project.context.root, "public", rel),
6517
- join13(project.context.root, rel)
6518
- ].find((path) => existsSync7(path));
6874
+ join17(project.context.root, "public", rel),
6875
+ join17(project.context.root, rel)
6876
+ ].find((path) => existsSync9(path));
6519
6877
  if (file) {
6520
6878
  return { alt, href, svg: readFileSync6(file, "utf-8") };
6521
6879
  }
@@ -6543,7 +6901,7 @@ var faviconType = (name) => {
6543
6901
  };
6544
6902
  var inlineDataUri = (file, type) => `data:${type};base64,${readFileSync6(file).toString("base64")}`;
6545
6903
  var defaultFavicon = () => ({
6546
- href: inlineDataUri(join13(BLUME_SRC, "assets", "icon.png"), "image/png"),
6904
+ href: inlineDataUri(join17(BLUME_SRC, "assets", "icon.png"), "image/png"),
6547
6905
  type: "image/png"
6548
6906
  });
6549
6907
  var APPLE_ICON_CANDIDATES = [
@@ -6555,13 +6913,13 @@ var APPLE_ICON_CANDIDATES = [
6555
6913
  var resolveIconFile = (project, candidates) => {
6556
6914
  const { root } = project.context;
6557
6915
  for (const name of candidates) {
6558
- if (existsSync7(join13(root, "public", name))) {
6916
+ if (existsSync9(join17(root, "public", name))) {
6559
6917
  return { href: `/${name}`, type: faviconType(name) };
6560
6918
  }
6561
6919
  }
6562
6920
  for (const name of candidates) {
6563
- const file = join13(root, name);
6564
- if (existsSync7(file)) {
6921
+ const file = join17(root, name);
6922
+ if (existsSync9(file)) {
6565
6923
  const type = faviconType(name);
6566
6924
  return { href: inlineDataUri(file, type ?? "image/x-icon"), type };
6567
6925
  }
@@ -6643,6 +7001,7 @@ var buildRuntimeData = (project) => {
6643
7001
  label
6644
7002
  }))
6645
7003
  } : null,
7004
+ icons: config.icons,
6646
7005
  imageZoom: config.markdown.imageZoom,
6647
7006
  logo: resolveLogo(project),
6648
7007
  mcp: config.mcp.enabled ? { name: config.mcp.name ?? config.title, route: config.mcp.route } : null,
@@ -6689,7 +7048,7 @@ var buildRuntimeData = (project) => {
6689
7048
  var planMcp = (project, srcDir) => {
6690
7049
  const { config } = project;
6691
7050
  const { route } = config.mcp;
6692
- const dir = join13(srcDir, "blume-mcp");
7051
+ const dir = join17(srcDir, "blume-mcp");
6693
7052
  const base = {
6694
7053
  dir,
6695
7054
  discoveryPages: [],
@@ -6713,11 +7072,11 @@ var planMcp = (project, srcDir) => {
6713
7072
  ...base,
6714
7073
  discoveryPages: [
6715
7074
  {
6716
- entrypoint: join13(dir, "discovery.ts"),
7075
+ entrypoint: join17(dir, "discovery.ts"),
6717
7076
  pattern: "/.well-known/mcp.json"
6718
7077
  },
6719
7078
  {
6720
- entrypoint: join13(dir, "server-card.ts"),
7079
+ entrypoint: join17(dir, "server-card.ts"),
6721
7080
  pattern: "/.well-known/mcp/server-card.json"
6722
7081
  }
6723
7082
  ],
@@ -6736,11 +7095,11 @@ var writeMcpFiles = async (project, plan, write) => {
6736
7095
  version: data.version
6737
7096
  };
6738
7097
  await Promise.all([
6739
- write(join13(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
7098
+ write(join17(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
6740
7099
  `),
6741
- write(join13(plan.srcDir, "pages", mcpPageFile(plan.route)), mcpEndpointTemplate(plan.route)),
6742
- write(join13(plan.dir, "discovery.ts"), staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))),
6743
- write(join13(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
7100
+ write(join17(plan.srcDir, "pages", mcpPageFile(plan.route)), mcpEndpointTemplate(plan.route)),
7101
+ write(join17(plan.dir, "discovery.ts"), staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))),
7102
+ write(join17(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
6744
7103
  ]);
6745
7104
  };
6746
7105
  var writeAskFiles = async (project, srcDir, write) => {
@@ -6750,16 +7109,16 @@ var writeAskFiles = async (project, srcDir, write) => {
6750
7109
  }
6751
7110
  const grounded = ask.provider !== "inkeep";
6752
7111
  if (grounded) {
6753
- await write(join13(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
7112
+ await write(join17(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
6754
7113
  `);
6755
7114
  }
6756
- await write(join13(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
7115
+ await write(join17(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
6757
7116
  };
6758
7117
  var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
6759
7118
  if (routeIsTaken(pages, contentPages, "/404")) {
6760
7119
  return;
6761
7120
  }
6762
- await write(join13(srcDir, "pages", "404.astro"), notFoundPageTemplate());
7121
+ await write(join17(srcDir, "pages", "404.astro"), notFoundPageTemplate());
6763
7122
  };
6764
7123
  var shouldGenerateChangelog = (project) => {
6765
7124
  const hasChangelog = project.graph.pages.some((page) => page.contentType === "changelog" && !(page.meta.draft || page.meta.sidebar.hidden));
@@ -6768,7 +7127,7 @@ var shouldGenerateChangelog = (project) => {
6768
7127
  return (hasChangelog || hasChangelogSource) && !changelogRouteTaken;
6769
7128
  };
6770
7129
  var buildComponentSlots = async (componentsFile) => {
6771
- const analysis = componentsFile ? analyzeComponentOverrides(await readFile7(componentsFile, "utf-8"), componentsFile) : null;
7130
+ const analysis = componentsFile ? analyzeComponentOverrides(await readFile10(componentsFile, "utf-8"), componentsFile) : null;
6772
7131
  return {
6773
7132
  plan: planComponentSlots(componentsFile, analysis),
6774
7133
  tags: analysis ? [...analysis.mdx, ...analysis.islands].map((entry) => entry.key) : [],
@@ -6778,14 +7137,15 @@ var buildComponentSlots = async (componentsFile) => {
6778
7137
  var generateRuntime = async (project) => {
6779
7138
  const { context, config } = project;
6780
7139
  const out = context.outDir;
6781
- const srcDir = join13(out, "src");
6782
- const dataPath = join13(srcDir, "generated", "data.json");
6783
- const themePath = join13(srcDir, "generated", "app.css");
6784
- const searchClientPath = join13(srcDir, "generated", "search-client.ts");
6785
- const examplesPath = join13(srcDir, "generated", "examples.ts");
7140
+ const srcDir = join17(out, "src");
7141
+ const dataPath = join17(srcDir, "generated", "data.json");
7142
+ const themePath = join17(srcDir, "generated", "app.css");
7143
+ const searchClientPath = join17(srcDir, "generated", "search-client.ts");
7144
+ const examplesPath = join17(srcDir, "generated", "examples.ts");
7145
+ const openapiPath = join17(srcDir, "generated", "openapi.json");
6786
7146
  const written = new Set;
6787
7147
  const write = (path, content) => {
6788
- written.add(normalize(path));
7148
+ written.add(normalize3(path));
6789
7149
  return writeIfChanged(path, content);
6790
7150
  };
6791
7151
  const depsLinkWarning = await ensureDepsLink(out);
@@ -6818,7 +7178,7 @@ var generateRuntime = async (project) => {
6818
7178
  const staged = collectStaged(project);
6819
7179
  const hasStaged = staged.size > 0;
6820
7180
  const structural = await Promise.all([
6821
- write(join13(out, "astro.config.mjs"), astroConfigTemplate({
7181
+ write(join17(out, "astro.config.mjs"), astroConfigTemplate({
6822
7182
  aliases: resolveTsconfigAliases(context.root),
6823
7183
  config,
6824
7184
  contentRoutes: project.manifest.routes.map((route) => route.path),
@@ -6828,24 +7188,25 @@ var generateRuntime = async (project) => {
6828
7188
  needsReact,
6829
7189
  needsSvelte,
6830
7190
  needsVue,
7191
+ openapiPath,
6831
7192
  pages,
6832
7193
  searchClientPath,
6833
7194
  themePath
6834
7195
  })),
6835
- write(join13(out, "package.json"), runtimePackageTemplate(runtimeDependencies({ config, needsReact, needsSvelte, needsVue }))),
6836
- write(join13(out, "tsconfig.json"), runtimeTsconfigTemplate()),
6837
- write(join13(srcDir, "env.d.ts"), envTemplate()),
6838
- write(join13(srcDir, "content.config.ts"), contentConfigTemplate({ config, context, staged: hasStaged })),
6839
- write(join13(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
7196
+ write(join17(out, "package.json"), runtimePackageTemplate(runtimeDependencies({ config, needsReact, needsSvelte, needsVue }))),
7197
+ write(join17(out, "tsconfig.json"), runtimeTsconfigTemplate()),
7198
+ write(join17(srcDir, "env.d.ts"), envTemplate()),
7199
+ write(join17(srcDir, "content.config.ts"), contentConfigTemplate({ config, context, staged: hasStaged })),
7200
+ write(join17(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
6840
7201
  askEnabled,
6841
7202
  exportEpub,
6842
7203
  exportPdf,
6843
7204
  mathEnabled: config.markdown.math,
6844
7205
  needsReact
6845
7206
  })),
6846
- write(join13(srcDir, "generated", "components.ts"), slotPlan.module),
6847
- write(join13(srcDir, "generated", "islands.ts"), islandMapTemplate(islandDiscovery.islands)),
6848
- write(join13(srcDir, "generated", "examples.ts"), exampleMapTemplate(exampleDiscovery.examples)),
7207
+ write(join17(srcDir, "generated", "components.ts"), slotPlan.module),
7208
+ write(join17(srcDir, "generated", "islands.ts"), islandMapTemplate(islandDiscovery.islands)),
7209
+ write(join17(srcDir, "generated", "examples.ts"), exampleMapTemplate(exampleDiscovery.examples)),
6849
7210
  write(themePath, tailwindEntryTemplate({
6850
7211
  configTokens: `${buildThemeCss(config.theme)}${buildFontsCss(config.theme.fonts)}`,
6851
7212
  sources: [
@@ -6856,16 +7217,16 @@ var generateRuntime = async (project) => {
6856
7217
  userTheme
6857
7218
  }))
6858
7219
  ]);
6859
- await Promise.all(islandDiscovery.islands.map((island) => write(join13(srcDir, "generated", "islands", `${island.name}.astro`), islandWrapperTemplate(island))));
6860
- await Promise.all(slotPlan.wrappers.map((wrapper) => write(join13(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content)));
6861
- await Promise.all(exampleDiscovery.examples.map((example) => write(join13(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example))));
7220
+ await Promise.all(islandDiscovery.islands.map((island) => write(join17(srcDir, "generated", "islands", `${island.name}.astro`), islandWrapperTemplate(island))));
7221
+ await Promise.all(slotPlan.wrappers.map((wrapper) => write(join17(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content)));
7222
+ await Promise.all(exampleDiscovery.examples.map((example) => write(join17(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example))));
6862
7223
  await writeAskFiles(project, srcDir, write);
6863
7224
  await writeMcpFiles(project, mcp, write);
6864
7225
  if (config.seo.og.enabled) {
6865
- await write(join13(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes));
7226
+ await write(join17(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes));
6866
7227
  }
6867
7228
  if (shouldGenerateChangelog(project)) {
6868
- await write(join13(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
7229
+ await write(join17(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
6869
7230
  askEnabled,
6870
7231
  exportEpub,
6871
7232
  exportPdf,
@@ -6877,27 +7238,27 @@ var generateRuntime = async (project) => {
6877
7238
  await write(searchClientPath, searchClientTemplate(config));
6878
7239
  if (servesStaticIndex(config.search.provider)) {
6879
7240
  const documents = await buildSearchDocuments(project);
6880
- await write(join13(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
7241
+ await write(join17(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
6881
7242
  `);
6882
- await write(join13(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
7243
+ await write(join17(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
6883
7244
  }
6884
7245
  if (config.search.provider === "mixedbread") {
6885
- await write(join13(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
7246
+ await write(join17(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
6886
7247
  }
6887
7248
  const rawMarkdown = await buildRawMarkdown(project);
6888
7249
  await Promise.all([
6889
- write(join13(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
7250
+ write(join17(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
6890
7251
  `),
6891
- write(join13(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate()),
6892
- write(join13(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate())
7252
+ write(join17(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate()),
7253
+ write(join17(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate())
6893
7254
  ]);
6894
7255
  const feeds = buildRssFeeds(project);
6895
7256
  if (feeds.length > 0) {
6896
7257
  const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
6897
7258
  await Promise.all([
6898
- write(join13(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
7259
+ write(join17(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
6899
7260
  `),
6900
- write(join13(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
7261
+ write(join17(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
6901
7262
  ]);
6902
7263
  }
6903
7264
  const warnings = [
@@ -6927,17 +7288,20 @@ var generateRuntime = async (project) => {
6927
7288
  }
6928
7289
  }
6929
7290
  warnings.push(...islandFrameworkWarnings(frameworks, context.root));
6930
- if (hasReferences(config)) {
7291
+ if (hasScalarReferences(config)) {
6931
7292
  const references = await buildReferenceFiles({
6932
7293
  config,
6933
7294
  contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
6934
7295
  root: context.root
6935
7296
  });
6936
7297
  warnings.push(...references.warnings);
6937
- await Promise.all(references.files.map((file) => write(join13(srcDir, "pages", file.pagePath), file.content)));
7298
+ await Promise.all(references.files.map((file) => write(join17(srcDir, "pages", file.pagePath), file.content)));
6938
7299
  }
6939
- await write(join13(srcDir, "generated", "data.json"), buildRuntimeData(project));
6940
- await write(join13(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
7300
+ await write(join17(srcDir, "generated", "data.json"), buildRuntimeData(project));
7301
+ const openApiSource2 = project.sources.find(isOpenApiSource);
7302
+ await write(openapiPath, `${JSON.stringify(openApiSource2 ? openApiSource2.openApiData() : {})}
7303
+ `);
7304
+ await write(join17(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
6941
7305
  `);
6942
7306
  await writeStagedContent(out, staged);
6943
7307
  await pruneOrphans(srcDir, written);
@@ -6945,12 +7309,12 @@ var generateRuntime = async (project) => {
6945
7309
  };
6946
7310
 
6947
7311
  // src/core/config.ts
6948
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "node:fs";
7312
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
6949
7313
 
6950
7314
  // src/core/bridge.ts
6951
- import { existsSync as existsSync8 } from "node:fs";
6952
- import { readFile as readFile9 } from "node:fs/promises";
6953
- import { join as join14 } from "pathe";
7315
+ import { existsSync as existsSync10 } from "node:fs";
7316
+ import { readFile as readFile12 } from "node:fs/promises";
7317
+ import { join as join18 } from "pathe";
6954
7318
 
6955
7319
  // src/migrate/mintlify/assets.ts
6956
7320
  var assetRefs = (config) => {
@@ -6985,8 +7349,8 @@ var assetSegments = (config) => {
6985
7349
  };
6986
7350
 
6987
7351
  // src/migrate/mintlify/config.ts
6988
- import { readFile as readFile8 } from "node:fs/promises";
6989
- import { dirname as dirname8, relative as relative7, resolve as resolve4 } from "pathe";
7352
+ import { readFile as readFile11 } from "node:fs/promises";
7353
+ import { dirname as dirname8, relative as relative7, resolve as resolve5 } from "pathe";
6990
7354
  var MINTLIFY_DEFAULT_IGNORES = [
6991
7355
  "**/_*",
6992
7356
  "**/.*",
@@ -7027,7 +7391,7 @@ var isInsideRoot = (root, candidate) => {
7027
7391
  };
7028
7392
  var readJsonFile = async (file) => {
7029
7393
  try {
7030
- return JSON.parse(await readFile8(file, "utf-8"));
7394
+ return JSON.parse(await readFile11(file, "utf-8"));
7031
7395
  } catch (error) {
7032
7396
  throw new BlumeError({
7033
7397
  code: "BLUME_MINTLIFY_CONFIG_INVALID",
@@ -7047,7 +7411,7 @@ var resolveRefs = async (value, options) => {
7047
7411
  }
7048
7412
  const ref = asString(object.$ref);
7049
7413
  if (ref) {
7050
- const refFile = resolve4(dirname8(options.file), ref);
7414
+ const refFile = resolve5(dirname8(options.file), ref);
7051
7415
  if (!isInsideRoot(options.root, refFile)) {
7052
7416
  throw new BlumeError({
7053
7417
  code: "BLUME_MINTLIFY_REF_OUTSIDE_ROOT",
@@ -7403,13 +7767,15 @@ var mintlifySelectors = (spec) => {
7403
7767
  };
7404
7768
  var mintignorePatterns = async (root) => {
7405
7769
  try {
7406
- const raw = await readFile8(resolve4(root, ".mintignore"), "utf-8");
7770
+ const raw = await readFile11(resolve5(root, ".mintignore"), "utf-8");
7407
7771
  return raw.split(`
7408
7772
  `).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).filter((line) => !line.startsWith("!")).map((line) => line.endsWith("/") ? `${line}**` : line);
7409
7773
  } catch {
7410
7774
  return [];
7411
7775
  }
7412
7776
  };
7777
+ var REDIRECT_PARAM = /:(?<name>[A-Za-z_]\w*)(?<modifier>[*+?])?/gu;
7778
+ var toAstroRedirectPath = (path) => path.replaceAll(REDIRECT_PARAM, (_match, name, modifier) => modifier === "*" || modifier === "+" ? `[...${name}]` : `[${name}]`);
7413
7779
  var mintlifyRedirects = (spec) => asArray(spec.redirects).flatMap((redirect) => {
7414
7780
  const object = asObject(redirect);
7415
7781
  if (!object) {
@@ -7420,8 +7786,72 @@ var mintlifyRedirects = (spec) => asArray(spec.redirects).flatMap((redirect) =>
7420
7786
  if (!from || !to) {
7421
7787
  return [];
7422
7788
  }
7423
- return [{ from, to }];
7789
+ return [{ from: toAstroRedirectPath(from), to: toAstroRedirectPath(to) }];
7424
7790
  });
7791
+ var openapiRouteFromDirectory = (value) => {
7792
+ const directory = normalizeDirectory(asString(value) ?? "");
7793
+ return directory.length > 0 ? `/${directory}` : undefined;
7794
+ };
7795
+ var openapiSourcesFromValue = (value, context) => {
7796
+ if (typeof value === "string") {
7797
+ if (value.length === 0 || API_ENDPOINT_REF.test(value)) {
7798
+ return [];
7799
+ }
7800
+ return [
7801
+ withoutUndefined({
7802
+ label: context.label,
7803
+ route: context.route,
7804
+ spec: value
7805
+ })
7806
+ ];
7807
+ }
7808
+ if (Array.isArray(value)) {
7809
+ return value.flatMap((item) => openapiSourcesFromValue(item, context));
7810
+ }
7811
+ const object = asObject(value);
7812
+ if (!object) {
7813
+ return [];
7814
+ }
7815
+ return openapiSourcesFromValue(object.source ?? object.openapi, {
7816
+ label: context.label,
7817
+ route: openapiRouteFromDirectory(object.directory) ?? context.route
7818
+ });
7819
+ };
7820
+ var mintlifyOpenapi = (spec) => {
7821
+ const drafts = [];
7822
+ const visit = (node) => {
7823
+ if (Array.isArray(node)) {
7824
+ for (const item of node) {
7825
+ visit(item);
7826
+ }
7827
+ return;
7828
+ }
7829
+ const object = asObject(node);
7830
+ if (!object) {
7831
+ return;
7832
+ }
7833
+ if (hasOwn(object, "openapi")) {
7834
+ drafts.push(...openapiSourcesFromValue(object.openapi, {
7835
+ label: labelForNavItem(object)
7836
+ }));
7837
+ }
7838
+ for (const children of childNavigationArrays(object)) {
7839
+ visit(children);
7840
+ }
7841
+ };
7842
+ visit(spec.navigation);
7843
+ drafts.push(...openapiSourcesFromValue(spec.openapi, {}));
7844
+ drafts.push(...openapiSourcesFromValue(asObject(spec.api)?.openapi, {}));
7845
+ const seen = new Set;
7846
+ const sources = drafts.flatMap((draft) => {
7847
+ if (seen.has(draft.spec)) {
7848
+ return [];
7849
+ }
7850
+ seen.add(draft.spec);
7851
+ return [draft];
7852
+ });
7853
+ return sources.length > 0 ? { enabled: true, sources } : undefined;
7854
+ };
7425
7855
  var mintlifyLogo = (value) => {
7426
7856
  if (typeof value === "string") {
7427
7857
  return value;
@@ -7453,6 +7883,12 @@ var mintlifyFavicon = (value) => {
7453
7883
  }
7454
7884
  return withoutUndefined({ dark, light });
7455
7885
  };
7886
+ var mintlifyIcons = (value) => {
7887
+ const library = asString(asObject(value)?.library);
7888
+ return {
7889
+ library: library === "lucide" || library === "tabler" ? library : "fontawesome"
7890
+ };
7891
+ };
7456
7892
  var mintlifyBanner = (value) => {
7457
7893
  const object = asObject(value);
7458
7894
  const content = object ? asString(object.content) : undefined;
@@ -7543,6 +7979,27 @@ var mintlifyMarkdown = (value, styling) => {
7543
7979
  schema: object?.schema === false ? false : undefined
7544
7980
  });
7545
7981
  };
7982
+ var FAMILY_TO_SLUG = Object.fromEntries(Object.entries(GOOGLE_FONTS).map(([slug, def]) => [
7983
+ def.family.toLowerCase(),
7984
+ slug
7985
+ ]));
7986
+ var fontSlugForFamily = (value) => {
7987
+ const family = asString(value);
7988
+ return family ? FAMILY_TO_SLUG[family.toLowerCase()] : undefined;
7989
+ };
7990
+ var mintlifyFonts = (value) => {
7991
+ const object = asObject(value);
7992
+ if (!object) {
7993
+ return;
7994
+ }
7995
+ const heading = asObject(object.heading);
7996
+ const body = asObject(object.body);
7997
+ const fonts = withoutUndefined({
7998
+ body: fontSlugForFamily(body?.family) ?? fontSlugForFamily(object.family),
7999
+ display: fontSlugForFamily(heading?.family) ?? fontSlugForFamily(object.family)
8000
+ });
8001
+ return Object.keys(fonts).length > 0 ? fonts : undefined;
8002
+ };
7546
8003
  var mintlifySeo = (value) => {
7547
8004
  const object = asObject(value);
7548
8005
  const metatags = asObject(object?.metatags);
@@ -7554,8 +8011,8 @@ var mintlifySeo = (value) => {
7554
8011
  };
7555
8012
  };
7556
8013
  var loadMintlifyConfig = async (root, file) => {
7557
- const projectRoot = resolve4(root);
7558
- const configFile = resolve4(file);
8014
+ const projectRoot = resolve5(root);
8015
+ const configFile = resolve5(file);
7559
8016
  const spec = asObject(await resolveRefs(await readJsonFile(configFile), {
7560
8017
  file: configFile,
7561
8018
  root: projectRoot,
@@ -7588,6 +8045,7 @@ var loadMintlifyConfig = async (root, file) => {
7588
8045
  },
7589
8046
  description: asString(spec.description),
7590
8047
  favicon: mintlifyFavicon(spec.favicon),
8048
+ icons: mintlifyIcons(spec.icons),
7591
8049
  logo: mintlifyLogo(spec.logo),
7592
8050
  markdown: mintlifyMarkdown(spec.markdown, styling),
7593
8051
  navigation: {
@@ -7597,6 +8055,7 @@ var loadMintlifyConfig = async (root, file) => {
7597
8055
  sidebarVariants: await mintlifySidebarVariants(spec),
7598
8056
  tabs: mintlifyTabs(spec)
7599
8057
  },
8058
+ openapi: mintlifyOpenapi(spec),
7600
8059
  redirects: mintlifyRedirects(spec),
7601
8060
  search: {
7602
8061
  indexing: {
@@ -7614,6 +8073,7 @@ var loadMintlifyConfig = async (root, file) => {
7614
8073
  backgroundDecoration: mintlifyBackgroundDecoration(spec.background),
7615
8074
  backgroundImage: backgroundImage.light,
7616
8075
  backgroundImageDark: backgroundImage.dark,
8076
+ fonts: mintlifyFonts(spec.fonts ?? spec.font),
7617
8077
  mode: appearance.default === "light" || appearance.default === "dark" || appearance.default === "system" ? appearance.default : "system",
7618
8078
  strict: appearance.strict === true
7619
8079
  },
@@ -7651,12 +8111,12 @@ var mintlifyI18n = (spec) => {
7651
8111
  // src/core/bridge.ts
7652
8112
  var MINTLIFY_CONFIG_FILES = ["docs.json", "mint.json"];
7653
8113
  var detectMintlifyBridge = async (root) => {
7654
- const configFile = MINTLIFY_CONFIG_FILES.map((name) => join14(root, name)).find((candidate) => existsSync8(candidate));
8114
+ const configFile = MINTLIFY_CONFIG_FILES.map((name) => join18(root, name)).find((candidate) => existsSync10(candidate));
7655
8115
  if (!configFile) {
7656
8116
  return null;
7657
8117
  }
7658
8118
  const config = await loadMintlifyConfig(root, configFile);
7659
- const spec = JSON.parse(await readFile9(configFile, "utf-8"));
8119
+ const spec = JSON.parse(await readFile12(configFile, "utf-8"));
7660
8120
  const i18n = mintlifyI18n(spec);
7661
8121
  if (i18n) {
7662
8122
  config.i18n = i18n;
@@ -7667,7 +8127,7 @@ var detectMintlifyBridge = async (root) => {
7667
8127
  const variables = config.variables ?? {};
7668
8128
  const root_ = config.content?.root ?? ".";
7669
8129
  const exclude = config.content?.exclude ?? [];
7670
- const assets = assetSegments(config).filter((segment) => segment !== "public" && existsSync8(join14(root, segment)));
8130
+ const assets = assetSegments(config).filter((segment) => segment !== "public" && existsSync10(join18(root, segment)));
7671
8131
  return {
7672
8132
  configFile,
7673
8133
  raw: {
@@ -7741,42 +8201,6 @@ var createModuleLoader = () => {
7741
8201
  };
7742
8202
  };
7743
8203
 
7744
- // src/core/project.ts
7745
- import { existsSync as existsSync9 } from "node:fs";
7746
- import { isAbsolute as isAbsolute4, join as join15, resolve as resolve5 } from "pathe";
7747
- var CONFIG_FILENAMES = [
7748
- "blume.config.ts",
7749
- "blume.config.mjs",
7750
- "blume.config.js"
7751
- ];
7752
- var THEME_FILENAMES = ["theme.css"];
7753
- var COMPONENTS_FILENAMES = ["components.tsx", "components.ts"];
7754
- var firstExisting = (root, names) => {
7755
- for (const name of names) {
7756
- const candidate = join15(root, name);
7757
- if (existsSync9(candidate)) {
7758
- return candidate;
7759
- }
7760
- }
7761
- return null;
7762
- };
7763
- var findConfigFile = (root) => firstExisting(root, CONFIG_FILENAMES);
7764
- var resolveProjectContext = (root, config) => {
7765
- const absoluteRoot = resolve5(root);
7766
- const contentRoot = isAbsolute4(config.content.root) ? config.content.root : join15(absoluteRoot, config.content.root);
7767
- const pagesPath = join15(absoluteRoot, config.content.pages);
7768
- const pagesRoot = existsSync9(pagesPath) ? pagesPath : null;
7769
- return {
7770
- componentsFile: firstExisting(absoluteRoot, COMPONENTS_FILENAMES),
7771
- configFile: findConfigFile(absoluteRoot),
7772
- contentRoot,
7773
- outDir: join15(absoluteRoot, ".blume"),
7774
- pagesRoot,
7775
- root: absoluteRoot,
7776
- themeFile: firstExisting(absoluteRoot, THEME_FILENAMES)
7777
- };
7778
- };
7779
-
7780
8204
  // src/core/schema.ts
7781
8205
  import { z as z2 } from "zod";
7782
8206
  var iconName = z2.string().min(1);
@@ -7806,7 +8230,17 @@ var changelogMetaSchema = z2.object({
7806
8230
  date: dateSchema.optional(),
7807
8231
  version: z2.string().optional()
7808
8232
  }).strict();
8233
+ var authorSchema = z2.union([
8234
+ z2.string(),
8235
+ z2.object({
8236
+ avatar: z2.string().optional(),
8237
+ image: z2.string().optional(),
8238
+ name: z2.string(),
8239
+ url: z2.string().optional()
8240
+ }).passthrough()
8241
+ ]);
7809
8242
  var pageMetaBaseSchema = z2.object({
8243
+ authors: z2.union([authorSchema, z2.array(authorSchema)]).optional(),
7810
8244
  changelog: changelogMetaSchema.optional(),
7811
8245
  date: dateSchema.optional(),
7812
8246
  deprecated: z2.boolean().default(false),
@@ -8247,7 +8681,10 @@ var openapiSourceSchema = z2.object({
8247
8681
  spec: z2.string()
8248
8682
  }).strict();
8249
8683
  var openapiConfigSchema = z2.object({
8684
+ codeSamples: z2.array(z2.string()).default(["curl", "js", "python"]),
8250
8685
  enabled: z2.boolean().default(false),
8686
+ expandSchemas: z2.boolean().default(false),
8687
+ renderer: z2.enum(["blume", "scalar"]).default("blume"),
8251
8688
  route: z2.string().default("/reference"),
8252
8689
  sources: z2.array(openapiSourceSchema).default([]),
8253
8690
  spec: z2.string().optional(),
@@ -8276,6 +8713,9 @@ var tocConfigSchema = z2.union([
8276
8713
  minLevel: value.minHeadingLevel ?? 2
8277
8714
  };
8278
8715
  });
8716
+ var iconsConfigSchema = z2.object({
8717
+ library: z2.enum(["lucide", "fontawesome", "tabler"]).default("lucide")
8718
+ }).strict();
8279
8719
  var blumeConfigSchema = z2.object({
8280
8720
  ai: aiConfigSchema.default({}),
8281
8721
  analytics: analyticsConfigSchema.optional(),
@@ -8290,6 +8730,7 @@ var blumeConfigSchema = z2.object({
8290
8730
  feedback: z2.boolean().default(true),
8291
8731
  github: githubConfigSchema.optional(),
8292
8732
  i18n: i18nConfigSchema.optional(),
8733
+ icons: iconsConfigSchema.default({}),
8293
8734
  lastModified: lastModifiedConfigSchema.default(false),
8294
8735
  logo: logoConfigSchema.optional(),
8295
8736
  markdown: markdownConfigSchema.default({}),
@@ -8331,7 +8772,7 @@ var loadConfig = async (root, options = {}) => {
8331
8772
  const sourceFile = bridge?.configFile ?? configFile;
8332
8773
  const parsed = blumeConfigSchema.safeParse(raw ?? {});
8333
8774
  if (!parsed.success) {
8334
- const source = sourceFile && existsSync10(sourceFile) ? readFileSync7(sourceFile, "utf-8") : undefined;
8775
+ const source = sourceFile && existsSync11(sourceFile) ? readFileSync7(sourceFile, "utf-8") : undefined;
8335
8776
  const diagnostics = diagnosticsFromZod(parsed.error, {
8336
8777
  code: "BLUME_CONFIG_INVALID",
8337
8778
  file: sourceFile ?? undefined,
@@ -8808,7 +9249,7 @@ var discoverFolderMeta = async (contentRoot) => {
8808
9249
  };
8809
9250
 
8810
9251
  // src/core/sources/normalize.ts
8811
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
9252
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
8812
9253
  import GithubSlugger from "github-slugger";
8813
9254
  import { extname as extname4 } from "pathe";
8814
9255
  var NUMERIC_PREFIX2 = /^\d+[-_.]/u;
@@ -8816,7 +9257,7 @@ var GROUP_FOLDER2 = /^\((?<label>.+)\)$/u;
8816
9257
  var WORD_SPLIT2 = /[-_]/u;
8817
9258
  var stripNumericPrefix = (segment) => segment.replace(NUMERIC_PREFIX2, "");
8818
9259
  var groupLabel = (segment) => segment.match(GROUP_FOLDER2)?.groups?.label ?? null;
8819
- var slugify2 = (text) => text.toLowerCase().trim().replaceAll(/[^\w\s-]/gu, "").replaceAll(/[\s_]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "");
9260
+ var slugify3 = (text) => text.toLowerCase().trim().replaceAll(/[^\w\s-]/gu, "").replaceAll(/[\s_]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "");
8820
9261
  var titleCase = (value) => value.split(WORD_SPLIT2).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
8821
9262
  var mapRoute = (relativePath) => {
8822
9263
  const withoutExt = relativePath.slice(0, relativePath.length - extname4(relativePath).length);
@@ -8934,7 +9375,7 @@ var normalizeEntry2 = (entry, ctx) => {
8934
9375
  const ext = format === "mdx" ? ".mdx" : ".md";
8935
9376
  const result = pageMetaSchema.safeParse(entry.data);
8936
9377
  if (!result.success) {
8937
- const source = entry.raw ?? (entry.sourcePath && existsSync11(entry.sourcePath) ? readFileSync8(entry.sourcePath, "utf-8") : undefined);
9378
+ const source = entry.raw ?? (entry.sourcePath && existsSync12(entry.sourcePath) ? readFileSync8(entry.sourcePath, "utf-8") : undefined);
8938
9379
  return {
8939
9380
  diagnostics: diagnosticsFromZod(result.error, {
8940
9381
  code: "BLUME_FRONTMATTER_INVALID",
@@ -8983,15 +9424,15 @@ var normalizeEntry2 = (entry, ctx) => {
8983
9424
  };
8984
9425
 
8985
9426
  // src/core/sources/resolve.ts
8986
- import { join as join24 } from "pathe";
9427
+ import { join as join26 } from "pathe";
8987
9428
 
8988
9429
  // src/core/sources/filesystem.ts
8989
- import { existsSync as existsSync12, watch as fsWatch } from "node:fs";
8990
- import { readFile as readFile10 } from "node:fs/promises";
8991
- import { extname as extname5, isAbsolute as isAbsolute5, join as join16, relative as relative10, resolve as resolve6 } from "pathe";
9430
+ import { existsSync as existsSync13, watch as fsWatch } from "node:fs";
9431
+ import { readFile as readFile13 } from "node:fs/promises";
9432
+ import { extname as extname5, isAbsolute as isAbsolute6, join as join19, relative as relative10, resolve as resolve6 } from "pathe";
8992
9433
  import { glob as glob6 } from "tinyglobby";
8993
9434
  var filesystemSource = (options) => {
8994
- const contentRoot = isAbsolute5(options.root) ? options.root : join16(resolve6(options.projectRoot), options.root);
9435
+ const contentRoot = isAbsolute6(options.root) ? options.root : join19(resolve6(options.projectRoot), options.root);
8995
9436
  const load2 = async () => {
8996
9437
  const files = await glob6(options.include, {
8997
9438
  absolute: true,
@@ -9001,7 +9442,7 @@ var filesystemSource = (options) => {
9001
9442
  });
9002
9443
  files.sort();
9003
9444
  const entries = await Promise.all(files.map(async (file) => {
9004
- const source = await readFile10(file, "utf-8");
9445
+ const source = await readFile13(file, "utf-8");
9005
9446
  const ext = extname5(file).toLowerCase();
9006
9447
  const format = ext === ".mdx" ? "mdx" : "md";
9007
9448
  const parsed = frontmatter_default(source);
@@ -9015,7 +9456,7 @@ var filesystemSource = (options) => {
9015
9456
  return { diagnostics: [], entries };
9016
9457
  };
9017
9458
  const validate = () => {
9018
- if (!existsSync12(contentRoot)) {
9459
+ if (!existsSync13(contentRoot)) {
9019
9460
  throw new BlumeError({
9020
9461
  code: options.missingCode ?? "BLUME_CONTENT_ROOT_MISSING",
9021
9462
  file: contentRoot,
@@ -9026,7 +9467,7 @@ var filesystemSource = (options) => {
9026
9467
  }
9027
9468
  };
9028
9469
  const watch = (onChange) => {
9029
- if (!existsSync12(contentRoot)) {
9470
+ if (!existsSync13(contentRoot)) {
9030
9471
  return () => {};
9031
9472
  }
9032
9473
  const watcher = fsWatch(contentRoot, { recursive: true }, onChange);
@@ -9037,97 +9478,21 @@ var filesystemSource = (options) => {
9037
9478
  load: load2,
9038
9479
  name: options.name,
9039
9480
  prefix: options.prefix,
9040
- read: (ref) => readFile10(join16(contentRoot, ref), "utf-8"),
9481
+ read: (ref) => readFile13(join19(contentRoot, ref), "utf-8"),
9041
9482
  staged: false,
9042
9483
  validate,
9043
9484
  watch
9044
9485
  };
9045
9486
  };
9046
9487
 
9047
- // src/core/sources/cache.ts
9048
- import { mkdir as mkdir3, readFile as readFile11, writeFile as writeFile3 } from "node:fs/promises";
9049
- import { join as join17 } from "pathe";
9050
- var hashText = (text) => {
9051
- let hash = 5381;
9052
- for (let i = 0;i < text.length; i += 1) {
9053
- hash = (hash * 33 + (text.codePointAt(i) ?? 0)) % 2147483647;
9054
- }
9055
- return hash.toString(36);
9056
- };
9057
- var entriesDigest = (entries) => hashText(entries.map((entry) => `${entry.ref}:${entry.hash ?? hashText(entry.body.text)}`).join("|"));
9058
- var pollingWatch = (load2, intervalSeconds) => (onChange) => {
9059
- let last = "";
9060
- const tick = async () => {
9061
- try {
9062
- const { entries } = await load2();
9063
- const next = entriesDigest(entries);
9064
- if (last && next !== last) {
9065
- onChange();
9066
- }
9067
- last = next;
9068
- } catch {}
9069
- };
9070
- const timer = setInterval(() => {
9071
- tick();
9072
- }, intervalSeconds * 1000);
9073
- return () => clearInterval(timer);
9074
- };
9075
- var snapshotCache = (cacheDir) => {
9076
- const file = join17(cacheDir, "entries.json");
9077
- return {
9078
- read: async () => {
9079
- try {
9080
- return JSON.parse(await readFile11(file, "utf-8"));
9081
- } catch {
9082
- return [];
9083
- }
9084
- },
9085
- write: async (entries) => {
9086
- try {
9087
- await mkdir3(cacheDir, { recursive: true });
9088
- await writeFile3(file, `${JSON.stringify(entries)}
9089
- `, "utf-8");
9090
- } catch {}
9091
- }
9092
- };
9093
- };
9094
- var loadWithCache = async (name, cache, fetchEntries, refresh = true) => {
9095
- if (!refresh) {
9096
- const cached3 = await cache.read();
9097
- if (cached3.length > 0) {
9098
- return { diagnostics: [], entries: cached3 };
9099
- }
9100
- }
9101
- try {
9102
- const entries = await fetchEntries();
9103
- await cache.write(entries);
9104
- return { diagnostics: [], entries };
9105
- } catch (error) {
9106
- const fallback = await cache.read();
9107
- if (fallback.length > 0) {
9108
- const diagnostic = {
9109
- code: "BLUME_SOURCE_OFFLINE",
9110
- message: `Source "${name}" could not be fetched (${error.message}); served ${fallback.length} cached entries.`,
9111
- severity: "warning"
9112
- };
9113
- return { diagnostics: [diagnostic], entries: fallback };
9114
- }
9115
- throw new BlumeError({
9116
- code: "BLUME_SOURCE_FETCH_FAILED",
9117
- message: `Source "${name}" failed to load and no cache is available: ${error.message}`,
9118
- severity: "error"
9119
- });
9120
- }
9121
- };
9122
-
9123
9488
  // src/core/sources/github-releases.ts
9124
9489
  var DEFAULT_BASE_URL = "https://api.github.com";
9125
9490
  var DEFAULT_LIMIT = 100;
9126
9491
  var PER_PAGE = 100;
9127
9492
  var LEADING_V = /^v/iu;
9128
- var NON_SLUG2 = /[^a-z0-9]+/gu;
9493
+ var NON_SLUG3 = /[^a-z0-9]+/gu;
9129
9494
  var EDGE_DASHES = /^-+|-+$/gu;
9130
- var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG2, "-").replaceAll(EDGE_DASHES, "");
9495
+ var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG3, "-").replaceAll(EDGE_DASHES, "");
9131
9496
  var githubHeaders = () => {
9132
9497
  const headers = new Headers({ Accept: "application/vnd.github+json" });
9133
9498
  const token = process.env.GITHUB_TOKEN;
@@ -9390,25 +9755,25 @@ var mdxRemoteSource = (options, ctx) => {
9390
9755
  };
9391
9756
 
9392
9757
  // src/core/sources/mintlify.ts
9393
- import { existsSync as existsSync14, watch as fsWatch2 } from "node:fs";
9394
- import { readFile as readFile13 } from "node:fs/promises";
9395
- import { isAbsolute as isAbsolute7, join as join20, relative as relative14, resolve as resolve8 } from "pathe";
9758
+ import { existsSync as existsSync15, watch as fsWatch2 } from "node:fs";
9759
+ import { readFile as readFile15 } from "node:fs/promises";
9760
+ import { isAbsolute as isAbsolute8, join as join22, relative as relative14, resolve as resolve8 } from "pathe";
9396
9761
  import { glob as glob7 } from "tinyglobby";
9397
9762
 
9398
9763
  // src/migrate/shared.ts
9399
- import { existsSync as existsSync13 } from "node:fs";
9400
- import { readFile as readFile12, writeFile as writeFile4 } from "node:fs/promises";
9401
- import { isAbsolute as isAbsolute6, join as join18, relative as relative11 } from "pathe";
9764
+ import { existsSync as existsSync14 } from "node:fs";
9765
+ import { readFile as readFile14, writeFile as writeFile5 } from "node:fs/promises";
9766
+ import { isAbsolute as isAbsolute7, join as join20, relative as relative11 } from "pathe";
9402
9767
  var isInsideRoot2 = (root, candidate) => {
9403
9768
  const rel = relative11(root, candidate);
9404
- return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
9769
+ return rel === "" || !rel.startsWith("..") && !isAbsolute7(rel);
9405
9770
  };
9406
9771
  var writeBlumeConfig = async (root, config) => {
9407
9772
  const body = `import { defineConfig } from "blume";
9408
9773
 
9409
9774
  export default defineConfig(${JSON.stringify(config, null, 2)});
9410
9775
  `;
9411
- await writeFile4(join18(root, "blume.config.ts"), body, "utf-8");
9776
+ await writeFile5(join20(root, "blume.config.ts"), body, "utf-8");
9412
9777
  };
9413
9778
  var BLUME_SCRIPTS = {
9414
9779
  build: "blume build",
@@ -9416,13 +9781,13 @@ var BLUME_SCRIPTS = {
9416
9781
  start: "blume preview"
9417
9782
  };
9418
9783
  var rewriteFrameworkScripts = async (root, cli, remove) => {
9419
- const pkgPath = join18(root, "package.json");
9420
- if (!existsSync13(pkgPath)) {
9784
+ const pkgPath = join20(root, "package.json");
9785
+ if (!existsSync14(pkgPath)) {
9421
9786
  return false;
9422
9787
  }
9423
9788
  let pkg;
9424
9789
  try {
9425
- pkg = JSON.parse(await readFile12(pkgPath, "utf-8"));
9790
+ pkg = JSON.parse(await readFile14(pkgPath, "utf-8"));
9426
9791
  } catch {
9427
9792
  return false;
9428
9793
  }
@@ -9445,12 +9810,12 @@ var rewriteFrameworkScripts = async (root, cli, remove) => {
9445
9810
  }
9446
9811
  if (changed) {
9447
9812
  pkg.scripts = next;
9448
- await writeFile4(pkgPath, `${JSON.stringify(pkg, null, 2)}
9813
+ await writeFile5(pkgPath, `${JSON.stringify(pkg, null, 2)}
9449
9814
  `, "utf-8");
9450
9815
  }
9451
9816
  return changed;
9452
9817
  };
9453
- var leftoverFiles = (root, candidates) => candidates.filter((candidate) => existsSync13(join18(root, candidate)));
9818
+ var leftoverFiles = (root, candidates) => candidates.filter((candidate) => existsSync14(join20(root, candidate)));
9454
9819
  var attribute = (attrs, name) => {
9455
9820
  const match = attrs.match(new RegExp(`\\b${name}=(?:"(?<dq>[^"]*)"|'(?<sq>[^']*)')`, "u"));
9456
9821
  return match?.groups?.dq ?? match?.groups?.sq;
@@ -9847,7 +10212,7 @@ var isLiteralObject = (value) => typeof value === "object" && value !== null &&
9847
10212
  var asLiteralArray = (value) => Array.isArray(value) ? value : undefined;
9848
10213
 
9849
10214
  // src/migrate/mintlify/content.ts
9850
- import { dirname as dirname10, join as join19, relative as relative12 } from "pathe";
10215
+ import { dirname as dirname10, join as join21, relative as relative12 } from "pathe";
9851
10216
  var CALLOUT_DIRECTIVES = {
9852
10217
  Check: "success",
9853
10218
  Danger: "danger",
@@ -9883,7 +10248,7 @@ var rewriteSnippetImports = (source, options) => {
9883
10248
  if (/\.mdx?$/u.test(importSource)) {
9884
10249
  return "";
9885
10250
  }
9886
- const target = join19(options.root, importSource.replace(/^\/+/u, ""));
10251
+ const target = join21(options.root, importSource.replace(/^\/+/u, ""));
9887
10252
  components.push(importSource.replace(/^\/+/u, ""));
9888
10253
  let rel = relative12(dirname10(options.filePath), target);
9889
10254
  if (!rel.startsWith(".")) {
@@ -9893,7 +10258,7 @@ var rewriteSnippetImports = (source, options) => {
9893
10258
  });
9894
10259
  return { components, source: next };
9895
10260
  };
9896
- var UNSUPPORTED_COMPONENTS = ["ParamField", "ResponseField"];
10261
+ var UNSUPPORTED_COMPONENTS = ["Update"];
9897
10262
  var unsupportedMintlifyComponents = (source) => UNSUPPORTED_COMPONENTS.filter((name) => new RegExp(`<${name}\\b`, "u").test(source));
9898
10263
 
9899
10264
  // src/migrate/mintlify/frontmatter.ts
@@ -10164,9 +10529,9 @@ var rewriteMintlifyMarkdownSnippets = async (source, options) => {
10164
10529
  throw new Error(snippetCycleMessage(options.root, file, options.trail ?? []));
10165
10530
  }
10166
10531
  seen.add(file);
10167
- const readFile13 = options.readFile ?? readFileFromDisk;
10532
+ const readFile15 = options.readFile ?? readFileFromDisk;
10168
10533
  try {
10169
- const raw = await readFile13(file);
10534
+ const raw = await readFile15(file);
10170
10535
  const content = frontmatter_default(raw).content.trim();
10171
10536
  const transformed = await rewriteMintlifyMarkdownSnippets(content, {
10172
10537
  ...options,
@@ -10207,7 +10572,7 @@ var rewriteMintlifyMarkdownSnippets = async (source, options) => {
10207
10572
  return await inlineAt(0, source);
10208
10573
  };
10209
10574
  var rewriteMintlifySnippetVariables = async (source, options) => {
10210
- const readFile13 = options.readFile ?? readFileFromDisk;
10575
+ const readFile15 = options.readFile ?? readFileFromDisk;
10211
10576
  const inlineImport = async (current, variableImport) => {
10212
10577
  const file = resolveSnippetPath({
10213
10578
  filePath: options.filePath,
@@ -10217,7 +10582,7 @@ var rewriteMintlifySnippetVariables = async (source, options) => {
10217
10582
  if (!file) {
10218
10583
  return current;
10219
10584
  }
10220
- const exports = collectStringExports(frontmatter_default(await readFile13(file)).content);
10585
+ const exports = collectStringExports(frontmatter_default(await readFile15(file)).content);
10221
10586
  let next = current;
10222
10587
  for (const name of variableImport.names) {
10223
10588
  const value = exports.get(name.imported);
@@ -10295,7 +10660,7 @@ var MINTLIFY_SOURCE_IGNORES = [
10295
10660
  "snippets/**"
10296
10661
  ];
10297
10662
  var mintlifySource = (options) => {
10298
- const contentRoot = isAbsolute7(options.root) ? options.root : join20(resolve8(options.projectRoot), options.root);
10663
+ const contentRoot = isAbsolute8(options.root) ? options.root : join22(resolve8(options.projectRoot), options.root);
10299
10664
  const ignore = [...new Set([...options.exclude, ...MINTLIFY_SOURCE_IGNORES])];
10300
10665
  const transform = (raw, file) => transformMintlifyContent(raw, {
10301
10666
  filePath: file,
@@ -10312,7 +10677,7 @@ var mintlifySource = (options) => {
10312
10677
  files.sort();
10313
10678
  const unsupported = new Set;
10314
10679
  const entries = await Promise.all(files.map(async (file) => {
10315
- const result = await transform(await readFile13(file, "utf-8"), file);
10680
+ const result = await transform(await readFile15(file, "utf-8"), file);
10316
10681
  for (const name of result.unsupported) {
10317
10682
  unsupported.add(name);
10318
10683
  }
@@ -10328,14 +10693,14 @@ var mintlifySource = (options) => {
10328
10693
  const diagnostics = unsupported.size > 0 ? [
10329
10694
  {
10330
10695
  code: "BLUME_MINTLIFY_UNSUPPORTED",
10331
- message: `Mintlify components without a Blume equivalent were left as-is: ${[...unsupported].toSorted().join(", ")}. Use the OpenAPI reference for API parameters.`,
10696
+ message: `Mintlify components without a Blume equivalent were left as-is: ${[...unsupported].toSorted().join(", ")}. Replace them by hand or provide a matching component.`,
10332
10697
  severity: "warning"
10333
10698
  }
10334
10699
  ] : [];
10335
10700
  return { diagnostics, entries };
10336
10701
  };
10337
10702
  const validate = () => {
10338
- if (!existsSync14(contentRoot)) {
10703
+ if (!existsSync15(contentRoot)) {
10339
10704
  throw new BlumeError({
10340
10705
  code: "BLUME_CONTENT_ROOT_MISSING",
10341
10706
  file: contentRoot,
@@ -10347,11 +10712,11 @@ var mintlifySource = (options) => {
10347
10712
  };
10348
10713
  const watch = (onChange) => {
10349
10714
  const disposers = [];
10350
- if (existsSync14(contentRoot)) {
10715
+ if (existsSync15(contentRoot)) {
10351
10716
  const watcher = fsWatch2(contentRoot, { recursive: true }, onChange);
10352
10717
  disposers.push(() => watcher.close());
10353
10718
  }
10354
- if (options.configFile && existsSync14(options.configFile)) {
10719
+ if (options.configFile && existsSync15(options.configFile)) {
10355
10720
  const watcher = fsWatch2(options.configFile, onChange);
10356
10721
  disposers.push(() => watcher.close());
10357
10722
  }
@@ -10362,8 +10727,8 @@ var mintlifySource = (options) => {
10362
10727
  };
10363
10728
  };
10364
10729
  const read = async (ref) => {
10365
- const file = join20(contentRoot, ref);
10366
- const result = await transform(await readFile13(file, "utf-8"), file);
10730
+ const file = join22(contentRoot, ref);
10731
+ const result = await transform(await readFile15(file, "utf-8"), file);
10367
10732
  return result.content;
10368
10733
  };
10369
10734
  return {
@@ -10380,11 +10745,11 @@ var mintlifySource = (options) => {
10380
10745
 
10381
10746
  // src/core/sources/notion.ts
10382
10747
  import { setTimeout as sleep } from "node:timers/promises";
10383
- import { join as join22 } from "pathe";
10748
+ import { join as join24 } from "pathe";
10384
10749
 
10385
10750
  // src/core/sources/assets.ts
10386
- import { mkdir as mkdir4, writeFile as writeFile5 } from "node:fs/promises";
10387
- import { extname as extname6, join as join21 } from "pathe";
10751
+ import { mkdir as mkdir4, writeFile as writeFile6 } from "node:fs/promises";
10752
+ import { extname as extname6, join as join23 } from "pathe";
10388
10753
  var MD_IMAGE = /!\[(?<alt>[^\]]*)\]\((?<url>[^)\s]+)\)/gu;
10389
10754
  var REMOTE = /^https?:\/\//u;
10390
10755
  var SAFE_EXT = /^\.[a-z0-9]+$/iu;
@@ -10413,7 +10778,7 @@ var materializeAssets = async (markdown, ctx) => {
10413
10778
  const bytes = new Uint8Array(await res.arrayBuffer());
10414
10779
  const file = `${hashText(url)}${extFor(url)}`;
10415
10780
  await mkdir4(ctx.assetsDir, { recursive: true });
10416
- await writeFile5(join21(ctx.assetsDir, file), bytes);
10781
+ await writeFile6(join23(ctx.assetsDir, file), bytes);
10417
10782
  rewrites.set(url, `${ctx.assetsBaseUrl}/${file}`);
10418
10783
  } catch (error) {
10419
10784
  diagnostics.push({
@@ -10529,8 +10894,8 @@ ${text}
10529
10894
  };
10530
10895
  var notionSource = (options, ctx) => {
10531
10896
  const props = options.properties ?? {};
10532
- const cache = snapshotCache(ctx?.cacheDir ?? join22(".blume", "cache", options.name));
10533
- const assetsDir = ctx?.assetsDir ?? join22(".blume", "public", "blume-assets", options.name);
10897
+ const cache = snapshotCache(ctx?.cacheDir ?? join24(".blume", "cache", options.name));
10898
+ const assetsDir = ctx?.assetsDir ?? join24(".blume", "public", "blume-assets", options.name);
10534
10899
  const assetsBaseUrl = ctx?.assetsBaseUrl ?? `/blume-assets/${options.name}`;
10535
10900
  let snapshot = new Map;
10536
10901
  const resolveClient = async () => {
@@ -10635,10 +11000,10 @@ ${rendered.join(`
10635
11000
  data.sidebar = { order };
10636
11001
  }
10637
11002
  const slugProp = richToMarkdown(page.properties[props.slug ?? "Slug"]?.rich_text);
10638
- const slug = slugify2(slugProp || title) || page.id;
11003
+ const slug = slugify3(slugProp || title) || page.id;
10639
11004
  return { data, slug };
10640
11005
  };
10641
- const toEntry = async (client, page) => {
11006
+ const toEntry2 = async (client, page) => {
10642
11007
  const { data, slug } = frontmatter(page);
10643
11008
  const mdx = await renderBlocks(client, await childrenOf(client, page.id));
10644
11009
  const assets = await materializeAssets(mdx, {
@@ -10667,7 +11032,7 @@ ${rendered.join(`
10667
11032
  database_id: options.database,
10668
11033
  start_cursor: cursor
10669
11034
  })));
10670
- const built = await Promise.all(pages.map((page) => toEntry(client, page)));
11035
+ const built = await Promise.all(pages.map((page) => toEntry2(client, page)));
10671
11036
  for (const item of built) {
10672
11037
  assetDiagnostics.push(...item.diagnostics);
10673
11038
  }
@@ -10698,7 +11063,7 @@ ${rendered.join(`
10698
11063
  };
10699
11064
 
10700
11065
  // src/core/sources/sanity.ts
10701
- import { join as join23 } from "pathe";
11066
+ import { join as join25 } from "pathe";
10702
11067
 
10703
11068
  // src/core/sources/portable-text.ts
10704
11069
  var HEADING_STYLES = {
@@ -10837,11 +11202,11 @@ var resolveClient = async (options, preview) => {
10837
11202
  };
10838
11203
  var sanitySource = (options, ctx) => {
10839
11204
  const fields = options.fields ?? {};
10840
- const cache = snapshotCache(ctx?.cacheDir ?? join23(".blume", "cache", options.name));
11205
+ const cache = snapshotCache(ctx?.cacheDir ?? join25(".blume", "cache", options.name));
10841
11206
  let snapshot = new Map;
10842
- const toEntry = (doc) => {
11207
+ const toEntry2 = (doc) => {
10843
11208
  const slugValue = asString2(getPath(doc, fields.slug ?? "slug.current")) ?? asString2(doc._id) ?? "untitled";
10844
- const slug = slugify2(slugValue) || slugify2(asString2(doc._id) ?? "") || "untitled";
11209
+ const slug = slugify3(slugValue) || slugify3(asString2(doc._id) ?? "") || "untitled";
10845
11210
  const data = {};
10846
11211
  const title = asString2(getPath(doc, fields.title ?? "title"));
10847
11212
  const description = asString2(getPath(doc, fields.description ?? "description"));
@@ -10873,7 +11238,7 @@ var sanitySource = (options, ctx) => {
10873
11238
  const result = await loadWithCache(options.name, cache, async () => {
10874
11239
  const client = await resolveClient(options, ctx?.preview ?? false);
10875
11240
  const docs = await client.fetch(options.query);
10876
- return docs.map(toEntry);
11241
+ return docs.map(toEntry2);
10877
11242
  }, ctx?.refresh ?? true);
10878
11243
  snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
10879
11244
  return result;
@@ -10912,8 +11277,8 @@ var uniqueNamer = () => {
10912
11277
  };
10913
11278
  var sourceContext = (context, name, runtime) => ({
10914
11279
  assetsBaseUrl: `/blume-assets/${name}`,
10915
- assetsDir: join24(context.outDir, "public", "blume-assets", name),
10916
- cacheDir: join24(context.outDir, "cache", name),
11280
+ assetsDir: join26(context.outDir, "public", "blume-assets", name),
11281
+ cacheDir: join26(context.outDir, "cache", name),
10917
11282
  mode: runtime.mode,
10918
11283
  preview: runtime.preview,
10919
11284
  projectRoot: context.root,
@@ -10995,7 +11360,7 @@ var baseName = (def) => {
10995
11360
  }
10996
11361
  return def.prefix ?? def.type;
10997
11362
  };
10998
- var resolveSources = (config, context, runtime) => {
11363
+ var contentSources = (config, context, runtime) => {
10999
11364
  const defs = config.content.sources;
11000
11365
  if (!defs || defs.length === 0) {
11001
11366
  return [
@@ -11011,6 +11376,14 @@ var resolveSources = (config, context, runtime) => {
11011
11376
  const nameFor = uniqueNamer();
11012
11377
  return defs.map((def) => buildSource(def, nameFor(baseName(def)), context, runtime));
11013
11378
  };
11379
+ var resolveSources = (config, context, runtime) => {
11380
+ const sources = contentSources(config, context, runtime);
11381
+ const references = blumeReferences(config);
11382
+ if (references.length > 0) {
11383
+ sources.push(openApiSource(references, sourceContext(context, "openapi", runtime)));
11384
+ }
11385
+ return sources;
11386
+ };
11014
11387
 
11015
11388
  // src/core/project-graph.ts
11016
11389
  var applyConfigOverrides = (config, overrides) => {
@@ -11039,7 +11412,9 @@ var scanProject = async (root, options = {}) => {
11039
11412
  });
11040
11413
  const { bridge } = configResult;
11041
11414
  const config = applyConfigOverrides(configResult.config, options.overrides);
11042
- const context = resolveProjectContext(root, config);
11415
+ const context = resolveProjectContext(root, config, {
11416
+ runtimeDir: options.runtimeDir
11417
+ });
11043
11418
  const sources = resolveSources(config, context, {
11044
11419
  mode,
11045
11420
  preview,
@@ -11107,8 +11482,8 @@ var scanProject = async (root, options = {}) => {
11107
11482
  };
11108
11483
 
11109
11484
  // src/cli/env.ts
11110
- import { existsSync as existsSync15, readFileSync as readFileSync9 } from "node:fs";
11111
- import { dirname as dirname12, join as join25, resolve as resolve9 } from "pathe";
11485
+ import { existsSync as existsSync16, readFileSync as readFileSync9 } from "node:fs";
11486
+ import { dirname as dirname12, join as join27, resolve as resolve9 } from "pathe";
11112
11487
  var ENV_LINE = /^\s*(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?<value>.*?)\s*$/u;
11113
11488
  var DOUBLE_QUOTED2 = /^"(?<body>[\s\S]*)"$/u;
11114
11489
  var SINGLE_QUOTED = /^'(?<body>[\s\S]*)'$/u;
@@ -11146,7 +11521,7 @@ var applyEnv = (parsed) => {
11146
11521
  };
11147
11522
  var loadFile = (path) => {
11148
11523
  try {
11149
- if (existsSync15(path)) {
11524
+ if (existsSync16(path)) {
11150
11525
  applyEnv(parseEnv(readFileSync9(path, "utf-8")));
11151
11526
  }
11152
11527
  } catch {}
@@ -11155,10 +11530,10 @@ var loadEnvFiles = (startDir) => {
11155
11530
  let dir = resolve9(startDir);
11156
11531
  let done = false;
11157
11532
  while (!done) {
11158
- loadFile(join25(dir, ".env.local"));
11159
- loadFile(join25(dir, ".env"));
11533
+ loadFile(join27(dir, ".env.local"));
11534
+ loadFile(join27(dir, ".env"));
11160
11535
  const parent = dirname12(dir);
11161
- done = existsSync15(join25(dir, ".git")) || parent === dir;
11536
+ done = existsSync16(join27(dir, ".git")) || parent === dir;
11162
11537
  dir = parent;
11163
11538
  }
11164
11539
  };
@@ -11231,7 +11606,8 @@ var prepareProject = async (options) => {
11231
11606
  mode: options.mode,
11232
11607
  overrides: options.overrides,
11233
11608
  preview: options.preview,
11234
- refresh: options.refresh
11609
+ refresh: options.refresh,
11610
+ runtimeDir: options.runtimeDir
11235
11611
  });
11236
11612
  } catch (error) {
11237
11613
  if (error instanceof BlumeError) {
@@ -11287,24 +11663,24 @@ var emitRedirectFiles = async (config, distDir) => {
11287
11663
  if (redirects.length === 0 || config.deployment.output !== "static") {
11288
11664
  return;
11289
11665
  }
11290
- await writeFile6(join26(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
11666
+ await writeFile7(join28(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
11291
11667
  const platformFiles = [
11292
11668
  { content: buildNetlifyRedirects(redirects), name: "_redirects" },
11293
11669
  { content: buildVercelConfig(redirects), name: "vercel.json" }
11294
11670
  ];
11295
- await Promise.all(platformFiles.map((file) => existsSync16(join26(distDir, file.name)) ? Promise.resolve() : writeFile6(join26(distDir, file.name), file.content, "utf-8")));
11671
+ await Promise.all(platformFiles.map((file) => existsSync17(join28(distDir, file.name)) ? Promise.resolve() : writeFile7(join28(distDir, file.name), file.content, "utf-8")));
11296
11672
  logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
11297
11673
  };
11298
11674
  var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${(bytes / 1024).toFixed(bytes < 1024 * 100 ? 1 : 0)} kB`;
11299
11675
  var astroAssets = async (distDir, ext) => {
11300
- const astroDir = join26(distDir, "_astro");
11301
- if (!existsSync16(astroDir)) {
11676
+ const astroDir = join28(distDir, "_astro");
11677
+ if (!existsSync17(astroDir)) {
11302
11678
  return [];
11303
11679
  }
11304
11680
  const entries = await readdir(astroDir);
11305
11681
  const files = entries.filter((name) => name.endsWith(`.${ext}`));
11306
11682
  const sized = await Promise.all(files.map(async (name) => {
11307
- const info = await stat(join26(astroDir, name));
11683
+ const info = await stat(join28(astroDir, name));
11308
11684
  return { name, size: info.size };
11309
11685
  }));
11310
11686
  return sized.toSorted((a, b) => b.size - a.size);
@@ -11346,6 +11722,58 @@ var enforceBudget = async (distDir, args) => {
11346
11722
  }
11347
11723
  return passed ? "pass" : "fail";
11348
11724
  };
11725
+ var publishBuildArtifacts = async (project, distDir, args) => {
11726
+ if (project.config.search.provider === "pagefind") {
11727
+ logger.start("Building search index");
11728
+ const indexed = await buildSearchIndex(distDir);
11729
+ logger.success(`Indexed ${indexed} page(s) for search`);
11730
+ }
11731
+ await syncSearchProvider(project, {
11732
+ start: (message) => logger.start(message),
11733
+ success: (message) => logger.success(message),
11734
+ warn: (message) => logger.warn(message)
11735
+ });
11736
+ if (project.config.ai.llmsTxt) {
11737
+ const { index, full } = await buildLlmsFiles(project);
11738
+ await Promise.all([
11739
+ writeFile7(join28(distDir, "llms.txt"), index, "utf-8"),
11740
+ writeFile7(join28(distDir, "llms-full.txt"), full, "utf-8")
11741
+ ]);
11742
+ logger.success("Generated llms.txt and llms-full.txt");
11743
+ }
11744
+ const sitemap = buildSitemap(project);
11745
+ if (sitemap && !existsSync17(join28(distDir, "sitemap.xml"))) {
11746
+ await writeFile7(join28(distDir, "sitemap.xml"), sitemap, "utf-8");
11747
+ logger.success("Generated sitemap.xml");
11748
+ }
11749
+ const robots = buildRobots(project);
11750
+ if (robots && !existsSync17(join28(distDir, "robots.txt"))) {
11751
+ await writeFile7(join28(distDir, "robots.txt"), robots, "utf-8");
11752
+ logger.success("Generated robots.txt");
11753
+ }
11754
+ await emitRedirectFiles(project.config, distDir);
11755
+ const { config } = project;
11756
+ const features = serverFeatures(config);
11757
+ logger.box([
11758
+ `Output ${config.deployment.output}`,
11759
+ `Adapter ${config.deployment.adapter ?? "none"}`,
11760
+ `Site ${config.deployment.site ?? "not set"}`,
11761
+ `Search ${config.search.provider}`,
11762
+ `Redirects ${config.redirects.length}`,
11763
+ `Sitemap ${sitemap ? "yes" : "no (set deployment.site)"}`,
11764
+ `Robots ${robots ? "yes" : "no"}`,
11765
+ `LLM files ${config.ai.llmsTxt ? "yes" : "no"}`,
11766
+ `Server features ${features.length > 0 ? features.join(", ") : "none"}`
11767
+ ].join(`
11768
+ `));
11769
+ if (args.analyze) {
11770
+ await reportBundleSizes(distDir);
11771
+ }
11772
+ if (await enforceBudget(distDir, args) === "fail") {
11773
+ process.exit(1);
11774
+ }
11775
+ logger.success(`Built to ${distDir}`);
11776
+ };
11349
11777
  var buildCommand = defineCommand2({
11350
11778
  args: {
11351
11779
  adapter: {
@@ -11368,6 +11796,10 @@ var buildCommand = defineCommand2({
11368
11796
  description: "Fail if total client JavaScript exceeds this many kB.",
11369
11797
  type: "string"
11370
11798
  },
11799
+ isolated: {
11800
+ description: "Build into an isolated .blume-verify runtime (and its own dist) so a running dev server and the real dist/ are untouched. For verifying changes while `blume dev` runs.",
11801
+ type: "boolean"
11802
+ },
11371
11803
  output: {
11372
11804
  description: "Output mode: static | server.",
11373
11805
  type: "string"
@@ -11384,7 +11816,11 @@ var buildCommand = defineCommand2({
11384
11816
  },
11385
11817
  async run({ args }) {
11386
11818
  const root = process.cwd();
11387
- refuseIfDevRunning(root, "building");
11819
+ const runtimeDir = args.isolated ? ".blume-verify" : process.env.BLUME_RUNTIME_DIR;
11820
+ refuseIfDevRunning(root, "building", runtimeDir);
11821
+ if (args.isolated) {
11822
+ await ensureGitignore(root, [".blume-verify/"]);
11823
+ }
11388
11824
  if (args.output && args.output !== "static" && args.output !== "server") {
11389
11825
  logger.error(`Invalid --output "${args.output}" (use static | server).`);
11390
11826
  process.exit(1);
@@ -11403,6 +11839,7 @@ var buildCommand = defineCommand2({
11403
11839
  },
11404
11840
  preview: args.preview,
11405
11841
  root,
11842
+ runtimeDir,
11406
11843
  strict: args.strict
11407
11844
  });
11408
11845
  logger.start(`Building ${project.graph.pages.length} page(s) (${project.config.deployment.output} output)`);
@@ -11410,68 +11847,27 @@ var buildCommand = defineCommand2({
11410
11847
  logLevel: "info",
11411
11848
  root: project.context.outDir
11412
11849
  });
11413
- const distDir = join26(root, "dist");
11414
- if (project.config.search.provider === "pagefind") {
11415
- logger.start("Building search index");
11416
- const indexed = await buildSearchIndex(distDir);
11417
- logger.success(`Indexed ${indexed} page(s) for search`);
11418
- }
11419
- await syncSearchProvider(project, {
11420
- start: (message) => logger.start(message),
11421
- success: (message) => logger.success(message),
11422
- warn: (message) => logger.warn(message)
11423
- });
11424
- if (project.config.ai.llmsTxt) {
11425
- const { index, full } = await buildLlmsFiles(project);
11426
- await Promise.all([
11427
- writeFile6(join26(distDir, "llms.txt"), index, "utf-8"),
11428
- writeFile6(join26(distDir, "llms-full.txt"), full, "utf-8")
11429
- ]);
11430
- logger.success("Generated llms.txt and llms-full.txt");
11431
- }
11432
- const sitemap = buildSitemap(project);
11433
- if (sitemap && !existsSync16(join26(distDir, "sitemap.xml"))) {
11434
- await writeFile6(join26(distDir, "sitemap.xml"), sitemap, "utf-8");
11435
- logger.success("Generated sitemap.xml");
11436
- }
11437
- const robots = buildRobots(project);
11438
- if (robots && !existsSync16(join26(distDir, "robots.txt"))) {
11439
- await writeFile6(join26(distDir, "robots.txt"), robots, "utf-8");
11440
- logger.success("Generated robots.txt");
11441
- }
11442
- await emitRedirectFiles(project.config, distDir);
11443
- const { config } = project;
11444
- const features = serverFeatures(config);
11445
- logger.box([
11446
- `Output ${config.deployment.output}`,
11447
- `Adapter ${config.deployment.adapter ?? "none"}`,
11448
- `Site ${config.deployment.site ?? "not set"}`,
11449
- `Search ${config.search.provider}`,
11450
- `Redirects ${config.redirects.length}`,
11451
- `Sitemap ${sitemap ? "yes" : "no (set deployment.site)"}`,
11452
- `Robots ${robots ? "yes" : "no"}`,
11453
- `LLM files ${config.ai.llmsTxt ? "yes" : "no"}`,
11454
- `Server features ${features.length > 0 ? features.join(", ") : "none"}`
11455
- ].join(`
11456
- `));
11457
- if (args.analyze) {
11458
- await reportBundleSizes(distDir);
11459
- }
11460
- if (await enforceBudget(distDir, args) === "fail") {
11461
- process.exit(1);
11850
+ const distDir = project.context.distDir ?? join28(root, "dist");
11851
+ if (runtimeDir) {
11852
+ logger.success(`Isolated build OK — output at ${distDir} (not published).`);
11853
+ return;
11462
11854
  }
11463
- logger.success(`Built to ${distDir}`);
11855
+ await publishBuildArtifacts(project, distDir, args);
11464
11856
  }
11465
11857
  });
11466
11858
 
11467
11859
  // src/cli/commands/check.ts
11468
- import { existsSync as existsSync17 } from "node:fs";
11860
+ import { existsSync as existsSync18 } from "node:fs";
11469
11861
  import { check } from "@astrojs/check";
11470
11862
  import { sync } from "astro";
11471
11863
  import { defineCommand as defineCommand3 } from "citty";
11472
- import { join as join27 } from "pathe";
11864
+ import { join as join29 } from "pathe";
11473
11865
  var checkCommand = defineCommand3({
11474
11866
  args: {
11867
+ isolated: {
11868
+ description: "Type-check in an isolated .blume-verify runtime so a running dev server is untouched. For verifying changes while `blume dev` runs.",
11869
+ type: "boolean"
11870
+ },
11475
11871
  preview: {
11476
11872
  description: "Include drafts and unpublished CMS content.",
11477
11873
  type: "boolean"
@@ -11487,21 +11883,27 @@ var checkCommand = defineCommand3({
11487
11883
  },
11488
11884
  async run({ args }) {
11489
11885
  const root = process.cwd();
11886
+ const runtimeDir = args.isolated ? ".blume-verify" : process.env.BLUME_RUNTIME_DIR;
11887
+ refuseIfDevRunning(root, "checking", runtimeDir);
11888
+ if (args.isolated) {
11889
+ await ensureGitignore(root, [".blume-verify/"]);
11890
+ }
11490
11891
  const project = await prepareProject({
11491
11892
  mode: "build",
11492
11893
  preview: args.preview,
11493
11894
  root,
11895
+ runtimeDir,
11494
11896
  strict: args.strict
11495
11897
  });
11496
11898
  const { outDir } = project.context;
11497
11899
  await sync({ logLevel: "warn", root: outDir });
11498
- const tsconfig = join27(root, "tsconfig.json");
11900
+ const tsconfig = join29(root, "tsconfig.json");
11499
11901
  logger.start(`Type-checking ${project.graph.pages.length} page(s)`);
11500
11902
  const failed = await check({
11501
11903
  minimumFailingSeverity: "error",
11502
11904
  minimumSeverity: "hint",
11503
11905
  root: outDir,
11504
- tsconfig: existsSync17(tsconfig) ? tsconfig : undefined,
11906
+ tsconfig: existsSync18(tsconfig) ? tsconfig : undefined,
11505
11907
  watch: false
11506
11908
  });
11507
11909
  if (failed) {
@@ -11518,7 +11920,7 @@ import { dev } from "astro";
11518
11920
  import { defineCommand as defineCommand4 } from "citty";
11519
11921
 
11520
11922
  // src/astro/static-assets.ts
11521
- import { extname as extname7, join as join28, relative as relative15, resolve as resolve10, sep } from "pathe";
11923
+ import { extname as extname7, join as join30, relative as relative15, resolve as resolve10, sep } from "pathe";
11522
11924
 
11523
11925
  // src/astro/integration.ts
11524
11926
  var overlayServer = null;
@@ -11744,15 +12146,19 @@ var doctorCommand = defineCommand5({
11744
12146
  });
11745
12147
 
11746
12148
  // src/cli/commands/eject.ts
11747
- import { readFile as readFile15, writeFile as writeFile8 } from "node:fs/promises";
12149
+ import { readFile as readFile17, writeFile as writeFile9 } from "node:fs/promises";
11748
12150
  import { defineCommand as defineCommand6 } from "citty";
11749
- import { join as join30, relative as relative17 } from "pathe";
12151
+ import { join as join32, relative as relative17 } from "pathe";
11750
12152
 
11751
12153
  // src/registry/eject.ts
11752
- import { existsSync as existsSync18 } from "node:fs";
11753
- import { cp, mkdir as mkdir5, readFile as readFile14, rm as rm2, writeFile as writeFile7 } from "node:fs/promises";
11754
- import { join as join29, relative as relative16 } from "pathe";
12154
+ import { existsSync as existsSync19 } from "node:fs";
12155
+ import { cp, mkdir as mkdir5, readFile as readFile16, rm as rm2, writeFile as writeFile8 } from "node:fs/promises";
12156
+ import { join as join31, relative as relative16 } from "pathe";
11755
12157
  var POSIX = (path) => path.split("\\").join("/");
12158
+ var ejectOpenApiData = (project) => {
12159
+ const source = project.sources.find(isOpenApiSource);
12160
+ return source ? source.openApiData() : {};
12161
+ };
11756
12162
  var askFiles = async (project, srcDir, genDir) => {
11757
12163
  const { ask } = project.config.ai;
11758
12164
  if (!ask?.enabled) {
@@ -11762,14 +12168,14 @@ var askFiles = async (project, srcDir, genDir) => {
11762
12168
  const files = [
11763
12169
  {
11764
12170
  content: askEndpointTemplate(resolveAskBackend(ask), grounded),
11765
- path: join29(srcDir, "pages", "api", "ask.ts")
12171
+ path: join31(srcDir, "pages", "api", "ask.ts")
11766
12172
  }
11767
12173
  ];
11768
12174
  if (grounded) {
11769
12175
  files.push({
11770
12176
  content: `${JSON.stringify(await buildAskData(project))}
11771
12177
  `,
11772
- path: join29(genDir, "ask-data.json")
12178
+ path: join31(genDir, "ask-data.json")
11773
12179
  });
11774
12180
  }
11775
12181
  return files;
@@ -11777,15 +12183,15 @@ var askFiles = async (project, srcDir, genDir) => {
11777
12183
  var eject = async (root) => {
11778
12184
  const project = await scanProject(root, { mode: "build" });
11779
12185
  const { context, config } = project;
11780
- const srcDir = join29(root, "src");
11781
- const genDir = join29(srcDir, "generated");
12186
+ const srcDir = join31(root, "src");
12187
+ const genDir = join31(srcDir, "generated");
11782
12188
  const askEnabled = config.ai.ask?.enabled ?? false;
11783
12189
  const exportPdf = config.export.pdf;
11784
12190
  const exportEpub = config.export.epub;
11785
12191
  const [pages, needsReactRaw, userTheme, rawMarkdown, islands, examples] = await Promise.all([
11786
12192
  context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
11787
12193
  detectNeedsReact(root),
11788
- context.themeFile ? readFile14(context.themeFile, "utf-8") : Promise.resolve(""),
12194
+ context.themeFile ? readFile16(context.themeFile, "utf-8") : Promise.resolve(""),
11789
12195
  buildRawMarkdown(project),
11790
12196
  discoverIslands(root),
11791
12197
  discoverExamples(root, config.examples)
@@ -11822,18 +12228,19 @@ var eject = async (root) => {
11822
12228
  needsReact,
11823
12229
  needsSvelte,
11824
12230
  needsVue,
12231
+ openapiPath: "./src/generated/openapi.json",
11825
12232
  pages: relPages,
11826
12233
  searchClientPath: "./src/generated/search-client.ts",
11827
12234
  themePath: "./src/generated/app.css"
11828
12235
  }),
11829
- path: join29(root, "astro.config.mjs")
12236
+ path: join31(root, "astro.config.mjs")
11830
12237
  },
11831
12238
  {
11832
12239
  content: runtimeTsconfigTemplate(),
11833
- path: join29(root, "tsconfig.json"),
12240
+ path: join31(root, "tsconfig.json"),
11834
12241
  skipIfExists: true
11835
12242
  },
11836
- { content: envTemplate(), path: join29(srcDir, "env.d.ts") },
12243
+ { content: envTemplate(), path: join31(srcDir, "env.d.ts") },
11837
12244
  {
11838
12245
  content: contentConfigTemplate({
11839
12246
  config,
@@ -11841,7 +12248,7 @@ var eject = async (root) => {
11841
12248
  staged: hasStaged,
11842
12249
  stagedBase: stagedDir
11843
12250
  }),
11844
- path: join29(srcDir, "content.config.ts")
12251
+ path: join31(srcDir, "content.config.ts")
11845
12252
  },
11846
12253
  {
11847
12254
  content: catchAllPageTemplate({
@@ -11851,19 +12258,19 @@ var eject = async (root) => {
11851
12258
  mathEnabled: config.markdown.math,
11852
12259
  needsReact
11853
12260
  }),
11854
- path: join29(srcDir, "pages", "[...slug].astro")
12261
+ path: join31(srcDir, "pages", "[...slug].astro")
11855
12262
  },
11856
12263
  {
11857
12264
  content: planComponentSlots(componentsImport, null).module,
11858
- path: join29(genDir, "components.ts")
12265
+ path: join31(genDir, "components.ts")
11859
12266
  },
11860
12267
  {
11861
12268
  content: islandMapTemplate(islands.islands),
11862
- path: join29(genDir, "islands.ts")
12269
+ path: join31(genDir, "islands.ts")
11863
12270
  },
11864
12271
  {
11865
12272
  content: exampleMapTemplate(examples.examples),
11866
- path: join29(genDir, "examples.ts")
12273
+ path: join31(genDir, "examples.ts")
11867
12274
  },
11868
12275
  {
11869
12276
  content: tailwindEntryTemplate({
@@ -11875,21 +12282,26 @@ var eject = async (root) => {
11875
12282
  twoslashCss: twoslashCss(),
11876
12283
  userTheme
11877
12284
  }),
11878
- path: join29(genDir, "app.css")
12285
+ path: join31(genDir, "app.css")
12286
+ },
12287
+ { content: buildRuntimeData(project), path: join31(genDir, "data.json") },
12288
+ {
12289
+ content: `${JSON.stringify(ejectOpenApiData(project))}
12290
+ `,
12291
+ path: join31(genDir, "openapi.json")
11879
12292
  },
11880
- { content: buildRuntimeData(project), path: join29(genDir, "data.json") },
11881
12293
  {
11882
12294
  content: `${JSON.stringify(rawMarkdown)}
11883
12295
  `,
11884
- path: join29(genDir, "raw-markdown.json")
12296
+ path: join31(genDir, "raw-markdown.json")
11885
12297
  },
11886
12298
  {
11887
12299
  content: rawMarkdownEndpointTemplate(),
11888
- path: join29(srcDir, "pages", "[...slug].md.ts")
12300
+ path: join31(srcDir, "pages", "[...slug].md.ts")
11889
12301
  },
11890
12302
  {
11891
12303
  content: rawMarkdownEndpointTemplate(),
11892
- path: join29(srcDir, "pages", "[...slug].mdx.ts")
12304
+ path: join31(srcDir, "pages", "[...slug].mdx.ts")
11893
12305
  }
11894
12306
  ];
11895
12307
  if (askEnabled) {
@@ -11898,34 +12310,34 @@ var eject = async (root) => {
11898
12310
  if (config.seo.og.enabled) {
11899
12311
  files.push({
11900
12312
  content: ogEndpointTemplate(customOgRoutes(pages, config.title)),
11901
- path: join29(srcDir, "pages", "og", "[...slug].png.ts")
12313
+ path: join31(srcDir, "pages", "og", "[...slug].png.ts")
11902
12314
  });
11903
12315
  }
11904
12316
  if (!routeIsTaken(pages, project.graph.pages, "/404")) {
11905
12317
  files.push({
11906
12318
  content: notFoundPageTemplate(),
11907
- path: join29(srcDir, "pages", "404.astro")
12319
+ path: join31(srcDir, "pages", "404.astro")
11908
12320
  });
11909
12321
  }
11910
12322
  files.push({
11911
12323
  content: searchClientTemplate(config),
11912
- path: join29(genDir, "search-client.ts")
12324
+ path: join31(genDir, "search-client.ts")
11913
12325
  });
11914
12326
  if (servesStaticIndex(config.search.provider)) {
11915
12327
  const documents = await buildSearchDocuments(project);
11916
12328
  files.push({
11917
12329
  content: `${JSON.stringify(documents)}
11918
12330
  `,
11919
- path: join29(genDir, "search.json")
12331
+ path: join31(genDir, "search.json")
11920
12332
  }, {
11921
12333
  content: searchEndpointTemplate(),
11922
- path: join29(srcDir, "pages", "blume-search.json.ts")
12334
+ path: join31(srcDir, "pages", "blume-search.json.ts")
11923
12335
  });
11924
12336
  }
11925
12337
  if (config.search.provider === "mixedbread") {
11926
12338
  files.push({
11927
12339
  content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
11928
- path: join29(srcDir, "pages", "api", "search.ts")
12340
+ path: join31(srcDir, "pages", "api", "search.ts")
11929
12341
  });
11930
12342
  }
11931
12343
  const feeds = buildRssFeeds(project);
@@ -11934,13 +12346,13 @@ var eject = async (root) => {
11934
12346
  files.push({
11935
12347
  content: `${JSON.stringify(feedXml)}
11936
12348
  `,
11937
- path: join29(genDir, "rss.json")
12349
+ path: join31(genDir, "rss.json")
11938
12350
  }, {
11939
12351
  content: rssEndpointTemplate(),
11940
- path: join29(srcDir, "pages", "[section]", "rss.xml.ts")
12352
+ path: join31(srcDir, "pages", "[section]", "rss.xml.ts")
11941
12353
  });
11942
12354
  }
11943
- if (hasReferences(config)) {
12355
+ if (hasScalarReferences(config)) {
11944
12356
  const references = await buildReferenceFiles({
11945
12357
  config,
11946
12358
  contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
@@ -11949,28 +12361,28 @@ var eject = async (root) => {
11949
12361
  for (const file of references.files) {
11950
12362
  files.push({
11951
12363
  content: file.content,
11952
- path: join29(srcDir, "pages", file.pagePath)
12364
+ path: join31(srcDir, "pages", file.pagePath)
11953
12365
  });
11954
12366
  }
11955
12367
  }
11956
12368
  files.push(...islands.islands.map((island) => ({
11957
12369
  content: islandWrapperTemplate(island),
11958
- path: join29(genDir, "islands", `${island.name}.astro`)
12370
+ path: join31(genDir, "islands", `${island.name}.astro`)
11959
12371
  })), ...examples.examples.map((example) => ({
11960
12372
  content: exampleWrapperTemplate(example),
11961
- path: join29(genDir, "examples", `${exampleSlug(example.path)}.astro`)
12373
+ path: join31(genDir, "examples", `${exampleSlug(example.path)}.astro`)
11962
12374
  })));
11963
12375
  for (const [entryId, content] of staged) {
11964
- files.push({ content, path: join29(root, stagedDir, entryId) });
12376
+ files.push({ content, path: join31(root, stagedDir, entryId) });
11965
12377
  }
11966
- const written = files.filter((file) => !(file.skipIfExists && existsSync18(file.path)));
12378
+ const written = files.filter((file) => !(file.skipIfExists && existsSync19(file.path)));
11967
12379
  await Promise.all(written.map(async (file) => {
11968
- await mkdir5(join29(file.path, ".."), { recursive: true });
11969
- await writeFile7(file.path, file.content, "utf-8");
12380
+ await mkdir5(join31(file.path, ".."), { recursive: true });
12381
+ await writeFile8(file.path, file.content, "utf-8");
11970
12382
  }));
11971
- const assetsSrc = join29(context.outDir, "public", "blume-assets");
11972
- if (existsSync18(assetsSrc)) {
11973
- await cp(assetsSrc, join29(root, "public", "blume-assets"), {
12383
+ const assetsSrc = join31(context.outDir, "public", "blume-assets");
12384
+ if (existsSync19(assetsSrc)) {
12385
+ await cp(assetsSrc, join31(root, "public", "blume-assets"), {
11974
12386
  recursive: true
11975
12387
  });
11976
12388
  }
@@ -11980,10 +12392,10 @@ var eject = async (root) => {
11980
12392
 
11981
12393
  // src/cli/commands/eject.ts
11982
12394
  var updatePackageScripts = async (root) => {
11983
- const pkgPath = join30(root, "package.json");
12395
+ const pkgPath = join32(root, "package.json");
11984
12396
  let pkg;
11985
12397
  try {
11986
- pkg = JSON.parse(await readFile15(pkgPath, "utf-8"));
12398
+ pkg = JSON.parse(await readFile17(pkgPath, "utf-8"));
11987
12399
  } catch {
11988
12400
  return;
11989
12401
  }
@@ -11994,7 +12406,7 @@ var updatePackageScripts = async (root) => {
11994
12406
  dev: "astro dev",
11995
12407
  preview: "astro preview"
11996
12408
  };
11997
- await writeFile8(pkgPath, `${JSON.stringify(pkg, null, 2)}
12409
+ await writeFile9(pkgPath, `${JSON.stringify(pkg, null, 2)}
11998
12410
  `, "utf-8");
11999
12411
  };
12000
12412
  var ejectCommand = defineCommand6({
@@ -12033,32 +12445,7 @@ The blume package remains importable.`);
12033
12445
  import { existsSync as existsSync20 } from "node:fs";
12034
12446
  import { mkdir as mkdir6, writeFile as writeFile10 } from "node:fs/promises";
12035
12447
  import { defineCommand as defineCommand7 } from "citty";
12036
- import { basename as basename4, dirname as dirname13, isAbsolute as isAbsolute8, join as join32, relative as relative18 } from "pathe";
12037
-
12038
- // src/core/gitignore.ts
12039
- import { existsSync as existsSync19 } from "node:fs";
12040
- import { readFile as readFile16, writeFile as writeFile9 } from "node:fs/promises";
12041
- import { join as join31 } from "pathe";
12042
- var gitignoreKey = (line) => line.trim().replace(/\/+$/u, "");
12043
- var ensureGitignore = async (root, entries) => {
12044
- const path = join31(root, ".gitignore");
12045
- const existing = existsSync19(path) ? await readFile16(path, "utf-8") : "";
12046
- const present = new Set(existing.split(`
12047
- `).map(gitignoreKey).filter(Boolean));
12048
- const added = entries.filter((entry) => !present.has(gitignoreKey(entry)));
12049
- if (added.length === 0) {
12050
- return [];
12051
- }
12052
- const gap = existing.length > 0 && !existing.endsWith(`
12053
- `) ? `
12054
- ` : "";
12055
- await writeFile9(path, `${existing}${gap}${added.join(`
12056
- `)}
12057
- `, "utf-8");
12058
- return added;
12059
- };
12060
-
12061
- // src/cli/commands/init.ts
12448
+ import { basename as basename4, dirname as dirname13, isAbsolute as isAbsolute9, join as join33, relative as relative18 } from "pathe";
12062
12449
  var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
12063
12450
  var packageTemplate = (name, version) => `{
12064
12451
  "name": ${JSON.stringify(name)},
@@ -12106,7 +12493,7 @@ var STARTERS = {
12106
12493
  files: (dir) => [
12107
12494
  {
12108
12495
  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`."),
12109
- path: join32(dir, "index.mdx")
12496
+ path: join33(dir, "index.mdx")
12110
12497
  }
12111
12498
  ]
12112
12499
  },
@@ -12121,7 +12508,7 @@ var STARTERS = {
12121
12508
  files: (dir) => [
12122
12509
  {
12123
12510
  content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
12124
- path: join32(dir, "index.mdx")
12511
+ path: join33(dir, "index.mdx")
12125
12512
  },
12126
12513
  {
12127
12514
  content: `---
@@ -12132,7 +12519,7 @@ date: 2026-01-01
12132
12519
 
12133
12520
  The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
12134
12521
  `,
12135
- path: join32(dir, "changelog", "v1-0-0.mdx")
12522
+ path: join33(dir, "changelog", "v1-0-0.mdx")
12136
12523
  }
12137
12524
  ]
12138
12525
  },
@@ -12145,7 +12532,7 @@ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries besid
12145
12532
  Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
12146
12533
 
12147
12534
  Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
12148
- path: join32(dir, "index.mdx")
12535
+ path: join33(dir, "index.mdx")
12149
12536
  }
12150
12537
  ]
12151
12538
  },
@@ -12156,11 +12543,11 @@ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
12156
12543
  content: page("Introduction", "Get started with the SDK.", `# Introduction
12157
12544
 
12158
12545
  Install the SDK and make your first call. See [Installation](/installation).`),
12159
- path: join32(dir, "index.mdx")
12546
+ path: join33(dir, "index.mdx")
12160
12547
  },
12161
12548
  {
12162
12549
  content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
12163
- path: join32(dir, "installation.mdx")
12550
+ path: join33(dir, "installation.mdx")
12164
12551
  }
12165
12552
  ]
12166
12553
  }
@@ -12207,7 +12594,7 @@ var initCommand = defineCommand7({
12207
12594
  async run({ args }) {
12208
12595
  const root = process.cwd();
12209
12596
  const contentDir = args["content-dir"] ?? "docs";
12210
- if (isAbsolute8(contentDir) || relative18(root, join32(root, contentDir)).startsWith("..")) {
12597
+ if (isAbsolute9(contentDir) || relative18(root, join33(root, contentDir)).startsWith("..")) {
12211
12598
  logger.error(`Invalid --content-dir "${contentDir}" (must be a path inside the project).`);
12212
12599
  process.exit(1);
12213
12600
  }
@@ -12222,9 +12609,9 @@ var initCommand = defineCommand7({
12222
12609
  process.exit(1);
12223
12610
  }
12224
12611
  const starter = STARTERS[template];
12225
- const createdPackage = await writeFileSafe(join32(root, "package.json"), packageTemplate(toPackageName(basename4(root)), getBlumeVersion()));
12226
- await writeFileSafe(join32(root, "blume.config.ts"), starter.config);
12227
- await Promise.all(starter.files(contentDir).map((file) => writeFileSafe(join32(root, file.path), file.content)));
12612
+ const createdPackage = await writeFileSafe(join33(root, "package.json"), packageTemplate(toPackageName(basename4(root)), getBlumeVersion()));
12613
+ await writeFileSafe(join33(root, "blume.config.ts"), starter.config);
12614
+ await Promise.all(starter.files(contentDir).map((file) => writeFileSafe(join33(root, file.path), file.content)));
12228
12615
  const ignored = await ensureGitignore(root, [".blume/", "dist/"]);
12229
12616
  if (ignored.length > 0) {
12230
12617
  logger.success(`Added ${ignored.join(", ")} to .gitignore`);
@@ -12266,14 +12653,14 @@ import { defineCommand as defineCommand8 } from "citty";
12266
12653
 
12267
12654
  // src/migrate/fumadocs/index.ts
12268
12655
  import { existsSync as existsSync24 } from "node:fs";
12269
- import { mkdir as mkdir8, readFile as readFile18, rename as rename3, rm as rm3, writeFile as writeFile12 } from "node:fs/promises";
12270
- import { dirname as dirname16, join as join35, relative as relative19 } from "pathe";
12656
+ import { mkdir as mkdir8, readFile as readFile19, rename as rename3, rm as rm3, writeFile as writeFile12 } from "node:fs/promises";
12657
+ import { dirname as dirname16, join as join36, relative as relative19 } from "pathe";
12271
12658
  import { glob as glob8 } from "tinyglobby";
12272
12659
 
12273
12660
  // src/migrate/fumadocs/config.ts
12274
12661
  import { existsSync as existsSync21 } from "node:fs";
12275
- import { readFile as readFile17 } from "node:fs/promises";
12276
- import { basename as basename5, dirname as dirname14, join as join33 } from "pathe";
12662
+ import { readFile as readFile18 } from "node:fs/promises";
12663
+ import { basename as basename5, dirname as dirname14, join as join34 } from "pathe";
12277
12664
  var SOURCE_FILES = [
12278
12665
  "lib/source.ts",
12279
12666
  "app/source.ts",
@@ -12303,7 +12690,7 @@ var GENERIC_NAMES = new Set([
12303
12690
  var gitRepoRoot = (start) => {
12304
12691
  let dir = start;
12305
12692
  for (;; ) {
12306
- if (existsSync21(join33(dir, ".git"))) {
12693
+ if (existsSync21(join34(dir, ".git"))) {
12307
12694
  return dir;
12308
12695
  }
12309
12696
  const parent = dirname14(dir);
@@ -12315,12 +12702,12 @@ var gitRepoRoot = (start) => {
12315
12702
  };
12316
12703
  var bareName = (name) => name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
12317
12704
  var readTitle = async (root) => {
12318
- const packageJson = join33(root, "package.json");
12705
+ const packageJson = join34(root, "package.json");
12319
12706
  if (!existsSync21(packageJson)) {
12320
12707
  return "Documentation";
12321
12708
  }
12322
12709
  try {
12323
- const parsed = JSON.parse(await readFile17(packageJson, "utf-8"));
12710
+ const parsed = JSON.parse(await readFile18(packageJson, "utf-8"));
12324
12711
  const { name } = parsed;
12325
12712
  if (typeof name !== "string" || !name.trim()) {
12326
12713
  return "Documentation";
@@ -12341,11 +12728,11 @@ var readTitle = async (root) => {
12341
12728
  };
12342
12729
  var scrapeBaseUrl = async (root) => {
12343
12730
  for (const candidate of SOURCE_FILES) {
12344
- const file = join33(root, candidate);
12731
+ const file = join34(root, candidate);
12345
12732
  if (!existsSync21(file)) {
12346
12733
  continue;
12347
12734
  }
12348
- const base = BASE_URL.exec(await readFile17(file, "utf-8"))?.groups?.base;
12735
+ const base = BASE_URL.exec(await readFile18(file, "utf-8"))?.groups?.base;
12349
12736
  if (base) {
12350
12737
  return base;
12351
12738
  }
@@ -12620,7 +13007,7 @@ var normalizeFumadocsPageMeta = (value) => {
12620
13007
  // src/migrate/fumadocs/groups.ts
12621
13008
  import { existsSync as existsSync23, statSync as statSync2 } from "node:fs";
12622
13009
  import { mkdir as mkdir7, rename as rename2, writeFile as writeFile11 } from "node:fs/promises";
12623
- import { basename as basename6, join as join34 } from "pathe";
13010
+ import { basename as basename6, join as join35 } from "pathe";
12624
13011
 
12625
13012
  // src/migrate/fumadocs/meta.ts
12626
13013
  var SEPARATOR = /^---(?<label>.*)---$/u;
@@ -12765,16 +13152,16 @@ var isDirectory = (path) => {
12765
13152
  }
12766
13153
  };
12767
13154
  var resolveEntry = (docsDir, name) => {
12768
- if (!isInsideRoot2(docsDir, join34(docsDir, name))) {
13155
+ if (!isInsideRoot2(docsDir, join35(docsDir, name))) {
12769
13156
  return null;
12770
13157
  }
12771
13158
  for (const ext of PAGE_EXTS) {
12772
- const file = join34(docsDir, `${name}${ext}`);
13159
+ const file = join35(docsDir, `${name}${ext}`);
12773
13160
  if (existsSync23(file)) {
12774
13161
  return { kind: "file", path: file };
12775
13162
  }
12776
13163
  }
12777
- const folder = join34(docsDir, name);
13164
+ const folder = join35(docsDir, name);
12778
13165
  return isDirectory(folder) ? { kind: "folder", path: folder } : null;
12779
13166
  };
12780
13167
  var humanize2 = (name) => name.split(WORD_SPLIT3).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
@@ -12809,7 +13196,7 @@ var moveItemIntoGroup = async (item, docsDir, groupDir, label, warnings) => {
12809
13196
  warnings.push(`Sidebar entry "${item.name}" in section "${label}" matched no page or folder; skipped.`);
12810
13197
  return null;
12811
13198
  }
12812
- const dest = join34(groupDir, basename6(resolved.path));
13199
+ const dest = join35(groupDir, basename6(resolved.path));
12813
13200
  if (existsSync23(dest)) {
12814
13201
  warnings.push(`Skipped moving "${item.name}" into section "${label}" (target already exists).`);
12815
13202
  return null;
@@ -12837,7 +13224,7 @@ var reshapeSection = async (section, docsDir, order, warnings) => {
12837
13224
  }
12838
13225
  return;
12839
13226
  }
12840
- const groupDir = join34(docsDir, `(${section.label})`);
13227
+ const groupDir = join35(docsDir, `(${section.label})`);
12841
13228
  const sectionKeys = [];
12842
13229
  for (const item of section.items) {
12843
13230
  const key = await moveItemIntoGroup(item, docsDir, groupDir, section.label, warnings);
@@ -12850,7 +13237,7 @@ var reshapeSection = async (section, docsDir, order, warnings) => {
12850
13237
  }
12851
13238
  order.push(section.label);
12852
13239
  if (sectionKeys.length > 1) {
12853
- await writeFile11(join34(groupDir, "meta.ts"), renderMetaModule({ pages: sectionKeys }), "utf-8");
13240
+ await writeFile11(join35(groupDir, "meta.ts"), renderMetaModule({ pages: sectionKeys }), "utf-8");
12854
13241
  }
12855
13242
  };
12856
13243
  var reshapeFumadocsGroups = async (structure, docsDir) => {
@@ -12883,7 +13270,7 @@ var FUMADOCS_LEFTOVERS = [
12883
13270
  ];
12884
13271
  var movePage = async (abs, base, root) => {
12885
13272
  const rel = relative19(base, abs);
12886
- const dest = join35(root, "docs", rel);
13273
+ const dest = join36(root, "docs", rel);
12887
13274
  if (existsSync24(dest)) {
12888
13275
  return {
12889
13276
  includeWarnings: [],
@@ -12893,7 +13280,7 @@ var movePage = async (abs, base, root) => {
12893
13280
  unsupported: []
12894
13281
  };
12895
13282
  }
12896
- const raw = await readFile18(abs, "utf-8");
13283
+ const raw = await readFile19(abs, "utf-8");
12897
13284
  const included = await inlineFumadocsIncludes(raw, {
12898
13285
  filePath: abs,
12899
13286
  root: base
@@ -12930,12 +13317,12 @@ var writeMeta = async (dest, meta, rel, warnings) => {
12930
13317
  };
12931
13318
  var convertMeta = async (abs, base, root) => {
12932
13319
  const rel = relative19(base, abs);
12933
- const raw = await readFile18(abs, "utf-8");
13320
+ const raw = await readFile19(abs, "utf-8");
12934
13321
  let parsed;
12935
13322
  try {
12936
13323
  parsed = JSON.parse(raw);
12937
13324
  } catch {
12938
- const dest2 = join35(root, "docs", rel);
13325
+ const dest2 = join36(root, "docs", rel);
12939
13326
  if (existsSync24(dest2)) {
12940
13327
  return [`Skipped ${rel} (target already exists)`];
12941
13328
  }
@@ -12946,8 +13333,8 @@ var convertMeta = async (abs, base, root) => {
12946
13333
  ];
12947
13334
  }
12948
13335
  const dir = dirname16(rel) === "." ? "" : dirname16(rel);
12949
- const docsDir = join35(root, "docs", dir);
12950
- const dest = join35(docsDir, "meta.ts");
13336
+ const docsDir = join36(root, "docs", dir);
13337
+ const dest = join36(docsDir, "meta.ts");
12951
13338
  const structure = parseFumadocsPages(parsed.pages);
12952
13339
  if (structure.hasSections && !existsSync24(dest)) {
12953
13340
  const self = translateFumadocsSelfMeta(parsed);
@@ -12996,7 +13383,7 @@ var summarizePages = (results) => {
12996
13383
  };
12997
13384
  };
12998
13385
  var cleanupSourceDirs = async (root) => {
12999
- const docs = join35(root, "content", "docs");
13386
+ const docs = join36(root, "content", "docs");
13000
13387
  if (existsSync24(docs)) {
13001
13388
  const remaining = await glob8(["**/*"], { cwd: docs, dot: true });
13002
13389
  if (remaining.length > 0) {
@@ -13006,7 +13393,7 @@ var cleanupSourceDirs = async (root) => {
13006
13393
  }
13007
13394
  await rm3(docs, { force: true, recursive: true });
13008
13395
  }
13009
- const content = join35(root, "content");
13396
+ const content = join36(root, "content");
13010
13397
  if (existsSync24(content)) {
13011
13398
  const remaining = await glob8(["**/*"], { cwd: content, dot: true });
13012
13399
  if (remaining.length === 0) {
@@ -13017,7 +13404,7 @@ var cleanupSourceDirs = async (root) => {
13017
13404
  };
13018
13405
  var migrateFumadocsProject = async (root) => {
13019
13406
  const { config, warnings: configWarnings } = await loadFumadocsConfig(root);
13020
- const base = join35(root, SOURCE_DIR);
13407
+ const base = join36(root, SOURCE_DIR);
13021
13408
  if (!existsSync24(base)) {
13022
13409
  await writeBlumeConfig(root, config);
13023
13410
  return {
@@ -13077,9 +13464,32 @@ var migrateFumadocsProject = async (root) => {
13077
13464
 
13078
13465
  // src/migrate/mintlify/index.ts
13079
13466
  import { existsSync as existsSync25 } from "node:fs";
13080
- import { mkdir as mkdir9, readFile as readFile19, rename as rename4, rm as rm4, stat as stat2, writeFile as writeFile13 } from "node:fs/promises";
13081
- import { dirname as dirname17, join as join36 } from "pathe";
13467
+ import { mkdir as mkdir9, readFile as readFile20, rename as rename4, rm as rm4, stat as stat2, writeFile as writeFile13 } from "node:fs/promises";
13468
+ import { dirname as dirname17, join as join37 } from "pathe";
13082
13469
  import { glob as glob9 } from "tinyglobby";
13470
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
13471
+ var hasFontFamily = (value) => {
13472
+ const object = asRecord(value);
13473
+ if (!object) {
13474
+ return false;
13475
+ }
13476
+ const named = (child) => typeof asRecord(child)?.family === "string";
13477
+ return typeof object.family === "string" || named(object.heading) || named(object.body);
13478
+ };
13479
+ var droppedChromeWarnings = (spec, config) => {
13480
+ const warnings = [];
13481
+ const navbar = asRecord(spec.navbar);
13482
+ if (navbar && (navbar.links || navbar.primary)) {
13483
+ warnings.push("Header links (navbar.links/navbar.primary) have no blume.config equivalent and were dropped; re-add them with navigation.tabs or a Header layout override.");
13484
+ }
13485
+ if (asRecord(spec.footer)?.socials) {
13486
+ warnings.push("Footer social links (footer.socials) have no blume.config equivalent and were dropped; add them with a Footer layout override.");
13487
+ }
13488
+ if (hasFontFamily(spec.fonts ?? spec.font) && !config.theme?.fonts) {
13489
+ warnings.push("docs.json font family isn't in Blume's curated Google Fonts set; set theme.fonts to a supported slug or add @font-face rules in theme.css.");
13490
+ }
13491
+ return warnings;
13492
+ };
13083
13493
  var prune = (value) => {
13084
13494
  if (Array.isArray(value)) {
13085
13495
  return value.map(prune);
@@ -13108,13 +13518,13 @@ var writeBlumeConfig2 = async (root, config) => {
13108
13518
 
13109
13519
  export default defineConfig(${JSON.stringify(prune(config), null, 2)});
13110
13520
  `;
13111
- await writeFile13(join36(root, "blume.config.ts"), body, "utf-8");
13521
+ await writeFile13(join37(root, "blume.config.ts"), body, "utf-8");
13112
13522
  };
13113
13523
  var relocateAssets = async (root, segments) => {
13114
13524
  const served = [];
13115
13525
  const moved = [];
13116
13526
  for (const segment of segments) {
13117
- const source = join36(root, segment);
13527
+ const source = join37(root, segment);
13118
13528
  if (!existsSync25(source) || segment === "public") {
13119
13529
  continue;
13120
13530
  }
@@ -13123,11 +13533,11 @@ var relocateAssets = async (root, segments) => {
13123
13533
  served.push(segment);
13124
13534
  continue;
13125
13535
  }
13126
- const dest = join36(root, "public", segment);
13536
+ const dest = join37(root, "public", segment);
13127
13537
  if (existsSync25(dest)) {
13128
13538
  continue;
13129
13539
  }
13130
- await mkdir9(join36(root, "public"), { recursive: true });
13540
+ await mkdir9(join37(root, "public"), { recursive: true });
13131
13541
  await rename4(source, dest);
13132
13542
  moved.push(segment);
13133
13543
  }
@@ -13148,7 +13558,7 @@ var applyRelocatedAssets = (config, assets, warnings) => {
13148
13558
  }
13149
13559
  };
13150
13560
  var cleanupSnippets = async (root, kept, warnings) => {
13151
- const dir = join36(root, "snippets");
13561
+ const dir = join37(root, "snippets");
13152
13562
  if (!existsSync25(dir)) {
13153
13563
  return;
13154
13564
  }
@@ -13169,11 +13579,11 @@ var cleanupSnippets = async (root, kept, warnings) => {
13169
13579
  };
13170
13580
  var migrateMintlifyProject = async (root) => {
13171
13581
  const warnings = [];
13172
- const configFile = existsSync25(join36(root, "docs.json")) ? join36(root, "docs.json") : join36(root, "mint.json");
13582
+ const configFile = existsSync25(join37(root, "docs.json")) ? join37(root, "docs.json") : join37(root, "mint.json");
13173
13583
  let config;
13174
13584
  if (existsSync25(configFile)) {
13175
13585
  config = await loadMintlifyConfig(root, configFile);
13176
- const spec = JSON.parse(await readFile19(configFile, "utf-8"));
13586
+ const spec = JSON.parse(await readFile20(configFile, "utf-8"));
13177
13587
  const i18n = mintlifyI18n(spec);
13178
13588
  if (i18n) {
13179
13589
  config.i18n = i18n;
@@ -13182,6 +13592,11 @@ var migrateMintlifyProject = async (root) => {
13182
13592
  }
13183
13593
  warnings.push(`Mapped ${i18n.locales.length} languages to i18n.locales (default: ${i18n.defaultLocale}); review the locale labels.`);
13184
13594
  }
13595
+ const openapiSources = config.openapi?.sources ?? [];
13596
+ if (openapiSources.length > 0) {
13597
+ warnings.push(`Mapped ${openapiSources.length} OpenAPI spec source(s) to openapi.sources (native reference renderer); verify each spec path or URL resolves.`);
13598
+ }
13599
+ warnings.push(...droppedChromeWarnings(spec, config));
13185
13600
  } else {
13186
13601
  warnings.push("No docs.json or mint.json found; writing a default config.");
13187
13602
  config = { content: { root: "." }, title: "Documentation" };
@@ -13204,7 +13619,7 @@ var migrateMintlifyProject = async (root) => {
13204
13619
  const unsupported = new Set;
13205
13620
  const keptComponents = new Set;
13206
13621
  for (const file of files) {
13207
- const raw = await readFile19(file, "utf-8");
13622
+ const raw = await readFile20(file, "utf-8");
13208
13623
  const result = await transformMintlifyContent(raw, {
13209
13624
  filePath: file,
13210
13625
  root,
@@ -13239,7 +13654,7 @@ var migrateMintlifyProject = async (root) => {
13239
13654
  warnings.push(`Dropped unsupported page frontmatter keys: ${[...removedKeys].join(", ")}.`);
13240
13655
  }
13241
13656
  if (unsupported.size > 0) {
13242
- warnings.push(`Components without a Blume equivalent need manual review (use the OpenAPI reference instead): ${[...unsupported].join(", ")}.`);
13657
+ warnings.push(`Components without a Blume equivalent need manual review: ${[...unsupported].join(", ")}.`);
13243
13658
  }
13244
13659
  warnings.push("Review blume.config.ts; navigation, theme, and chrome were mapped from docs.json.");
13245
13660
  return { moved, warnings };
@@ -13247,8 +13662,8 @@ var migrateMintlifyProject = async (root) => {
13247
13662
 
13248
13663
  // src/migrate/nextra/index.ts
13249
13664
  import { existsSync as existsSync26 } from "node:fs";
13250
- import { mkdir as mkdir10, readFile as readFile20, rename as rename5, rm as rm5, writeFile as writeFile14 } from "node:fs/promises";
13251
- import { basename as basename7, dirname as dirname18, extname as extname8, join as join37, relative as relative20 } from "pathe";
13665
+ import { mkdir as mkdir10, readFile as readFile21, rename as rename5, rm as rm5, writeFile as writeFile14 } from "node:fs/promises";
13666
+ import { basename as basename7, dirname as dirname18, extname as extname8, join as join38, relative as relative20 } from "pathe";
13252
13667
  import { glob as glob10 } from "tinyglobby";
13253
13668
 
13254
13669
  // src/migrate/nextra/content.ts
@@ -13530,7 +13945,7 @@ var planMetas = (metas, index) => {
13530
13945
  plan.metaByDir.set(dir, conversion.folderMeta);
13531
13946
  plan.consumedMetas.push(meta.abs);
13532
13947
  for (const [slug, title] of Object.entries(conversion.folderTitles)) {
13533
- plan.folderTitleByDir.set(normalizeDir(join37(dir, slug)), title);
13948
+ plan.folderTitleByDir.set(normalizeDir(join38(dir, slug)), title);
13534
13949
  }
13535
13950
  for (const [slug, label] of Object.entries(conversion.pageLabels)) {
13536
13951
  const pageAbs = index.pagesByDir.get(dir)?.get(slug);
@@ -13555,11 +13970,11 @@ var planMetas = (metas, index) => {
13555
13970
  };
13556
13971
  var movePage2 = async (abs, options) => {
13557
13972
  const rel = relative20(options.base, abs);
13558
- const dest = join37(options.root, "docs", rel);
13973
+ const dest = join38(options.root, "docs", rel);
13559
13974
  if (existsSync26(dest)) {
13560
13975
  return { moved: 0, removed: [], skipped: rel, unsupported: [] };
13561
13976
  }
13562
- const raw = await readFile20(abs, "utf-8");
13977
+ const raw = await readFile21(abs, "utf-8");
13563
13978
  const text = rewriteNextraCallouts(stripNextraImports(raw));
13564
13979
  const unsupported = unsupportedNextraComponents(text);
13565
13980
  const parsed = frontmatter_default(text);
@@ -13608,7 +14023,7 @@ var writeFolderMetas = async (root, plan) => {
13608
14023
  if (Object.keys(finalMeta).length === 0) {
13609
14024
  return;
13610
14025
  }
13611
- const dest = join37(root, "docs", dir, "meta.ts");
14026
+ const dest = join38(root, "docs", dir, "meta.ts");
13612
14027
  await mkdir10(dirname18(dest), { recursive: true });
13613
14028
  await writeFile14(dest, `import { defineMeta } from "blume";
13614
14029
 
@@ -13619,7 +14034,7 @@ export default defineMeta(${JSON.stringify(finalMeta, null, 2)});
13619
14034
  var relocateUnparseableMetas = async (root, metas) => {
13620
14035
  const warnings = [];
13621
14036
  await Promise.all(metas.map(async ({ abs, rel }) => {
13622
- const dest = join37(root, "docs", rel);
14037
+ const dest = join38(root, "docs", rel);
13623
14038
  if (existsSync26(dest)) {
13624
14039
  warnings.push(`Skipped ${rel} (target already exists)`);
13625
14040
  return;
@@ -13632,7 +14047,7 @@ var relocateUnparseableMetas = async (root, metas) => {
13632
14047
  };
13633
14048
  var buildConfig = (tabs) => tabs.length > 0 ? { navigation: { tabs }, title: "Documentation" } : { title: "Documentation" };
13634
14049
  var migrateNextraProject = async (root) => {
13635
- const sourceDir = SOURCE_DIRS.find((dir) => existsSync26(join37(root, dir)));
14050
+ const sourceDir = SOURCE_DIRS.find((dir) => existsSync26(join38(root, dir)));
13636
14051
  if (!sourceDir) {
13637
14052
  await writeBlumeConfig(root, { title: "Documentation" });
13638
14053
  return {
@@ -13642,7 +14057,7 @@ var migrateNextraProject = async (root) => {
13642
14057
  ]
13643
14058
  };
13644
14059
  }
13645
- const base = join37(root, sourceDir);
14060
+ const base = join38(root, sourceDir);
13646
14061
  const pageFiles = await glob10([PAGE_GLOB2], {
13647
14062
  absolute: true,
13648
14063
  cwd: base,
@@ -13657,7 +14072,7 @@ var migrateNextraProject = async (root) => {
13657
14072
  const metas = await Promise.all(metaFiles.map(async (abs) => ({
13658
14073
  abs,
13659
14074
  ext: extname8(abs),
13660
- raw: await readFile20(abs, "utf-8"),
14075
+ raw: await readFile21(abs, "utf-8"),
13661
14076
  rel: relative20(base, abs)
13662
14077
  })));
13663
14078
  const plan = planMetas(metas, index);
@@ -13684,14 +14099,14 @@ var migrateNextraProject = async (root) => {
13684
14099
 
13685
14100
  // src/migrate/starlight/index.ts
13686
14101
  import { existsSync as existsSync28 } from "node:fs";
13687
- import { readFile as readFile22, writeFile as writeFile15 } from "node:fs/promises";
13688
- import { join as join39 } from "pathe";
14102
+ import { readFile as readFile23, writeFile as writeFile15 } from "node:fs/promises";
14103
+ import { join as join40 } from "pathe";
13689
14104
  import { glob as glob11 } from "tinyglobby";
13690
14105
 
13691
14106
  // src/migrate/starlight/config.ts
13692
14107
  import { existsSync as existsSync27 } from "node:fs";
13693
- import { readFile as readFile21 } from "node:fs/promises";
13694
- import { join as join38 } from "pathe";
14108
+ import { readFile as readFile22 } from "node:fs/promises";
14109
+ import { join as join39 } from "pathe";
13695
14110
  var CONFIG_FILES = [
13696
14111
  "astro.config.mjs",
13697
14112
  "astro.config.mts",
@@ -13722,7 +14137,7 @@ var extractStarlightOptions = (source) => {
13722
14137
  return isLiteralObject(parsed) ? parsed : "unparseable";
13723
14138
  };
13724
14139
  var loadStarlightConfig = async (root) => {
13725
- const file = CONFIG_FILES.map((name) => join38(root, name)).find((path) => existsSync27(path));
14140
+ const file = CONFIG_FILES.map((name) => join39(root, name)).find((path) => existsSync27(path));
13726
14141
  if (!file) {
13727
14142
  return {
13728
14143
  options: {},
@@ -13731,7 +14146,7 @@ var loadStarlightConfig = async (root) => {
13731
14146
  ]
13732
14147
  };
13733
14148
  }
13734
- const source = await readFile21(file, "utf-8");
14149
+ const source = await readFile22(file, "utf-8");
13735
14150
  const result = extractStarlightOptions(source);
13736
14151
  if (result === "missing") {
13737
14152
  return {
@@ -14129,7 +14544,7 @@ var starlightI18n = (options) => {
14129
14544
  // src/migrate/starlight/index.ts
14130
14545
  var CONTENT_DIR = "src/content/docs";
14131
14546
  var transformPage = async (file) => {
14132
- const raw = await readFile22(file, "utf-8");
14547
+ const raw = await readFile23(file, "utf-8");
14133
14548
  let text = stripStarlightImports(raw);
14134
14549
  text = rewriteStarlightAsides(text);
14135
14550
  text = rewriteStarlightComponents(text);
@@ -14151,7 +14566,7 @@ var migrateStarlightProject = async (root) => {
14151
14566
  config.i18n = i18n;
14152
14567
  warnings.push(`Mapped ${i18n.locales.length} locale(s) to i18n (default: ${i18n.defaultLocale}); review the locale labels.`);
14153
14568
  }
14154
- const base = join39(root, CONTENT_DIR);
14569
+ const base = join40(root, CONTENT_DIR);
14155
14570
  if (!existsSync28(base)) {
14156
14571
  await writeBlumeConfig(root, config);
14157
14572
  return {
@@ -14243,7 +14658,7 @@ var migrateCommand = defineCommand8({
14243
14658
  import { existsSync as existsSync29 } from "node:fs";
14244
14659
  import { preview } from "astro";
14245
14660
  import { defineCommand as defineCommand9 } from "citty";
14246
- import { join as join40 } from "pathe";
14661
+ import { join as join41 } from "pathe";
14247
14662
  var previewCommand = defineCommand9({
14248
14663
  args: {
14249
14664
  host: { description: "Network host to bind.", type: "string" },
@@ -14257,7 +14672,7 @@ var previewCommand = defineCommand9({
14257
14672
  const root = process.cwd();
14258
14673
  const { config } = await loadConfig(root);
14259
14674
  const context = resolveProjectContext(root, config);
14260
- if (!existsSync29(join40(context.outDir, "astro.config.mjs"))) {
14675
+ if (!existsSync29(join41(context.outDir, "astro.config.mjs"))) {
14261
14676
  logger.error("No build found. Run `blume build` first.");
14262
14677
  process.exit(1);
14263
14678
  }
@@ -14275,7 +14690,7 @@ var previewCommand = defineCommand9({
14275
14690
  // src/cli/commands/sync.ts
14276
14691
  import { rm as rm6 } from "node:fs/promises";
14277
14692
  import { defineCommand as defineCommand10 } from "citty";
14278
- import { join as join41 } from "pathe";
14693
+ import { join as join42 } from "pathe";
14279
14694
  var syncCommand = defineCommand10({
14280
14695
  args: {
14281
14696
  force: {
@@ -14297,7 +14712,7 @@ var syncCommand = defineCommand10({
14297
14712
  if (args.force) {
14298
14713
  const { config } = await loadConfig(root);
14299
14714
  const context = resolveProjectContext(root, config);
14300
- await rm6(join41(context.outDir, "cache"), { force: true, recursive: true });
14715
+ await rm6(join42(context.outDir, "cache"), { force: true, recursive: true });
14301
14716
  logger.info("Cleared source cache.");
14302
14717
  }
14303
14718
  await prepareProject({
@@ -14314,11 +14729,11 @@ var syncCommand = defineCommand10({
14314
14729
  // src/cli/commands/validate.ts
14315
14730
  import { existsSync as existsSync31 } from "node:fs";
14316
14731
  import { defineCommand as defineCommand11 } from "citty";
14317
- import { join as join43 } from "pathe";
14732
+ import { join as join44 } from "pathe";
14318
14733
 
14319
14734
  // src/core/links.ts
14320
14735
  import { existsSync as existsSync30 } from "node:fs";
14321
- import { basename as basename8, join as join42 } from "pathe";
14736
+ import { basename as basename8, join as join43 } from "pathe";
14322
14737
  var HTTP = /^https?:\/\//iu;
14323
14738
  var PROTOCOL_RELATIVE = /^\/\//u;
14324
14739
  var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
@@ -14331,10 +14746,10 @@ var STATUS_GONE = 410;
14331
14746
  var STATUS_METHOD_NOT_ALLOWED = 405;
14332
14747
  var STATUS_NOT_IMPLEMENTED = 501;
14333
14748
  var assetIsPresent = (resolved, ctx) => {
14334
- if (ctx.publicDir && existsSync30(join42(ctx.publicDir, resolved))) {
14749
+ if (ctx.publicDir && existsSync30(join43(ctx.publicDir, resolved))) {
14335
14750
  return true;
14336
14751
  }
14337
- return ctx.assetMounts.some((mount) => (resolved === mount.url || resolved.startsWith(`${mount.url}/`)) && existsSync30(join42(mount.dir, resolved.slice(mount.url.length))));
14752
+ return ctx.assetMounts.some((mount) => (resolved === mount.url || resolved.startsWith(`${mount.url}/`)) && existsSync30(join43(mount.dir, resolved.slice(mount.url.length))));
14338
14753
  };
14339
14754
  var isIndexPage = (page2) => {
14340
14755
  const ref = page2.source?.ref ?? page2.sourcePath ?? "";
@@ -14579,7 +14994,7 @@ var validateCommand = defineCommand11({
14579
14994
  try {
14580
14995
  const project = await scanProject(root, { mode: "build" });
14581
14996
  diagnostics.push(...project.diagnostics);
14582
- const publicDir = join43(root, "public");
14997
+ const publicDir = join44(root, "public");
14583
14998
  diagnostics.push(...await validateLinks(project.graph, {
14584
14999
  assetMounts: resolveAssetMounts(root, project.config.content.assets),
14585
15000
  checkExternal: Boolean(args.external),
@@ -14644,5 +15059,5 @@ process.on("unhandledRejection", (error) => {
14644
15059
  });
14645
15060
  runMain(main);
14646
15061
 
14647
- //# debugId=114B8B7438537BD664756E2164756E21
15062
+ //# debugId=432F59E1AFBFB0A064756E2164756E21
14648
15063
  //# sourceMappingURL=index.js.map