blume 0.4.0 → 0.5.1

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 (81) hide show
  1. package/dist/cli/index.js +1170 -820
  2. package/dist/cli/index.js.map +32 -27
  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 -41
  6. package/dist/types/core/types.d.ts +7 -6
  7. package/dist/types/migrate/mintlify/config.d.ts +14 -0
  8. package/docs/01-quickstart.mdx +6 -2
  9. package/docs/02-deployment.mdx +3 -1
  10. package/docs/advanced/api-reference.mdx +37 -23
  11. package/docs/advanced/bridge.mdx +76 -0
  12. package/docs/advanced/custom-pages.mdx +3 -1
  13. package/docs/advanced/meta.ts +8 -1
  14. package/docs/advanced/migrate.mdx +123 -0
  15. package/docs/configuration/ai.mdx +3 -1
  16. package/docs/configuration/analytics.mdx +3 -1
  17. package/docs/configuration/export.mdx +6 -2
  18. package/docs/configuration/index.mdx +1 -1
  19. package/docs/configuration/seo.mdx +3 -1
  20. package/docs/content/components.mdx +55 -2
  21. package/docs/content/i18n.mdx +6 -2
  22. package/docs/content/islands.mdx +6 -2
  23. package/docs/content/meta.mdx +3 -1
  24. package/docs/content/syntax.mdx +40 -14
  25. package/docs/index.mdx +2 -2
  26. package/docs/reference/cli.mdx +29 -1
  27. package/docs/reference/frontmatter.mdx +5 -0
  28. package/package.json +11 -1
  29. package/src/astro/generate.ts +18 -9
  30. package/src/astro/templates.ts +28 -4
  31. package/src/cli/commands/build.ts +107 -63
  32. package/src/cli/commands/check.ts +20 -0
  33. package/src/cli/dev-lock.ts +13 -5
  34. package/src/cli/prepare.ts +3 -0
  35. package/src/components/BlumePage.astro +6 -0
  36. package/src/components/Icon.astro +13 -10
  37. package/src/components/content/ApiField.astro +75 -0
  38. package/src/components/content/ParamField.astro +39 -0
  39. package/src/components/content/RequestField.astro +23 -0
  40. package/src/components/content/ResponseField.astro +23 -0
  41. package/src/components/content/Step.astro +1 -1
  42. package/src/components/layout/Breadcrumbs.astro +7 -2
  43. package/src/components/layout/NavTree.astro +24 -8
  44. package/src/components/layout/RootLayout.astro +56 -34
  45. package/src/components/layout/Search.astro +1 -1
  46. package/src/components/openapi/ApiOverview.astro +84 -0
  47. package/src/components/openapi/MethodBadge.astro +28 -0
  48. package/src/components/openapi/Operation.astro +140 -0
  49. package/src/components/openapi/ParametersTable.astro +97 -0
  50. package/src/components/openapi/RequestBody.astro +58 -0
  51. package/src/components/openapi/RequestPanel.astro +169 -0
  52. package/src/components/openapi/Responses.astro +91 -0
  53. package/src/components/openapi/SchemaProperty.astro +118 -0
  54. package/src/components/openapi/SchemaTable.astro +86 -0
  55. package/src/components/openapi/helpers.ts +238 -0
  56. package/src/components/openapi/panel.ts +59 -0
  57. package/src/components/openapi/snippets.ts +201 -0
  58. package/src/core/builtin-tags.ts +5 -0
  59. package/src/core/data.ts +2 -0
  60. package/src/core/graph.ts +0 -3
  61. package/src/core/nav-diagnostics.ts +2 -12
  62. package/src/core/navigation.ts +0 -10
  63. package/src/core/project-graph.ts +5 -1
  64. package/src/core/project.ts +25 -3
  65. package/src/core/schema.ts +47 -14
  66. package/src/core/sources/mintlify.ts +1 -1
  67. package/src/core/sources/resolve.ts +28 -6
  68. package/src/core/types.ts +7 -7
  69. package/src/migrate/mintlify/config.ts +190 -97
  70. package/src/migrate/mintlify/content.ts +24 -2
  71. package/src/migrate/mintlify/index.ts +76 -2
  72. package/src/migrate/mintlify/transform.ts +2 -0
  73. package/src/openapi/model.ts +174 -0
  74. package/src/openapi/parse.ts +48 -0
  75. package/src/openapi/references.ts +164 -0
  76. package/src/openapi/render-mdx.ts +76 -0
  77. package/src/openapi/scalar.ts +15 -103
  78. package/src/openapi/source.ts +140 -0
  79. package/src/registry/eject.ts +15 -2
  80. package/src/theme/chrome-icons.ts +22 -0
  81. 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) {
@@ -3589,16 +3616,13 @@ var collectIcons = (navigation) => {
3589
3616
  push(item.icon, `selector "${item.label}"`);
3590
3617
  }
3591
3618
  }
3592
- const sidebars = [
3593
- navigation.sidebar,
3594
- ...navigation.sidebarVariants.map((variant) => variant.sidebar)
3595
- ];
3619
+ const sidebars = [navigation.sidebar];
3596
3620
  for (const sidebar of sidebars) {
3597
3621
  for (const node of flattenNodes(sidebar)) {
3598
3622
  push(node.icon, `"${node.label}"`);
3599
3623
  }
3600
3624
  }
3601
- return icons2;
3625
+ return icons;
3602
3626
  };
3603
3627
  var validateNavIcons = (navigation) => {
3604
3628
  const seen = new Set;
@@ -3665,11 +3689,7 @@ var duplicateLabelDiagnostics = (navigation) => {
3665
3689
  }
3666
3690
  };
3667
3691
  const sidebars = [
3668
- { nodes: navigation.sidebar, where: "at the top level" },
3669
- ...navigation.sidebarVariants.map((variant) => ({
3670
- nodes: variant.sidebar,
3671
- where: `in the "${variant.path}" section`
3672
- }))
3692
+ { nodes: navigation.sidebar, where: "at the top level" }
3673
3693
  ];
3674
3694
  for (const { nodes, where } of sidebars) {
3675
3695
  checkLevel(nodes, where);
@@ -3681,10 +3701,7 @@ var hiddenInSidebarDiagnostics = (navigation, pages) => {
3681
3701
  if (hidden.size === 0) {
3682
3702
  return [];
3683
3703
  }
3684
- const sidebars = [
3685
- navigation.sidebar,
3686
- ...navigation.sidebarVariants.map((variant) => variant.sidebar)
3687
- ];
3704
+ const sidebars = [navigation.sidebar];
3688
3705
  const diagnostics = [];
3689
3706
  const seen = new Set;
3690
3707
  for (const sidebar of sidebars) {
@@ -3708,10 +3725,10 @@ var validateNavStructure = (navigation, pages) => [
3708
3725
  ];
3709
3726
 
3710
3727
  // 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";
3728
+ import { existsSync as existsSync7, readFileSync as readFileSync3, statSync } from "node:fs";
3729
+ import { createRequire as createRequire3 } from "node:module";
3713
3730
  import { pathToFileURL } from "node:url";
3714
- import { dirname as dirname5, isAbsolute as isAbsolute2, join as join7, resolve as resolve3 } from "pathe";
3731
+ import { dirname as dirname5, isAbsolute as isAbsolute3, join as join9, resolve as resolve4 } from "pathe";
3715
3732
  var stripJsonComments = (text) => {
3716
3733
  let out = "";
3717
3734
  let inString = false;
@@ -3765,16 +3782,16 @@ var isFile = (path) => {
3765
3782
  }
3766
3783
  };
3767
3784
  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)
3785
+ if (spec.startsWith(".") || isAbsolute3(spec)) {
3786
+ const candidates = spec.endsWith(".json") ? [resolve4(fromDir, spec)] : [
3787
+ resolve4(fromDir, `${spec}.json`),
3788
+ resolve4(fromDir, spec, "tsconfig.json"),
3789
+ resolve4(fromDir, spec)
3773
3790
  ];
3774
3791
  return candidates.find(isFile) ?? null;
3775
3792
  }
3776
3793
  try {
3777
- const require_ = createRequire2(pathToFileURL(join7(fromDir, "_.js")).href);
3794
+ const require_ = createRequire3(pathToFileURL(join9(fromDir, "_.js")).href);
3778
3795
  for (const sub of [`${spec}/tsconfig.json`, spec]) {
3779
3796
  try {
3780
3797
  return require_.resolve(sub);
@@ -3784,7 +3801,7 @@ var resolveExtends = (spec, fromDir) => {
3784
3801
  return null;
3785
3802
  };
3786
3803
  var loadPaths = (file, seen) => {
3787
- if (seen.has(file) || !existsSync5(file)) {
3804
+ if (seen.has(file) || !existsSync7(file)) {
3788
3805
  return null;
3789
3806
  }
3790
3807
  seen.add(file);
@@ -3796,7 +3813,7 @@ var loadPaths = (file, seen) => {
3796
3813
  if (options.paths && typeof options.paths === "object") {
3797
3814
  const baseUrl = typeof options.baseUrl === "string" ? options.baseUrl : ".";
3798
3815
  return {
3799
- baseDir: resolve3(dirname5(file), baseUrl),
3816
+ baseDir: resolve4(dirname5(file), baseUrl),
3800
3817
  paths: options.paths
3801
3818
  };
3802
3819
  }
@@ -3823,10 +3840,10 @@ var toAlias = (key, value, baseDir) => {
3823
3840
  if (find === "" || find === "*") {
3824
3841
  return null;
3825
3842
  }
3826
- return { find, replacement: resolve3(baseDir, target) };
3843
+ return { find, replacement: resolve4(baseDir, target) };
3827
3844
  };
3828
3845
  var resolveTsconfigAliases = (root) => {
3829
- const entry = ["tsconfig.json", "jsconfig.json"].map((name) => join7(root, name)).find((file) => existsSync5(file));
3846
+ const entry = ["tsconfig.json", "jsconfig.json"].map((name) => join9(root, name)).find((file) => existsSync7(file));
3830
3847
  if (!entry) {
3831
3848
  return {};
3832
3849
  }
@@ -3922,19 +3939,97 @@ ${items}
3922
3939
  `;
3923
3940
  };
3924
3941
 
3942
+ // src/openapi/references.ts
3943
+ var NON_SLUG = /[^a-z0-9]+/gu;
3944
+ var SLUG_EDGES = /^-+|-+$/gu;
3945
+ var ROUTE_EDGES = /^\/+|\/+$/gu;
3946
+ var TRAILING_SLASH = /\/+$/u;
3947
+ var slugify = (text) => text.toLowerCase().replace(NON_SLUG, "-").replace(SLUG_EDGES, "");
3948
+ var normalizeRoute = (route) => {
3949
+ const trimmed = route.trim();
3950
+ const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
3951
+ const noTrailing = withSlash.replace(TRAILING_SLASH, "");
3952
+ return noTrailing === "" ? "/" : noTrailing;
3953
+ };
3954
+ var routeSlug = (route) => slugify(route.replace(ROUTE_EDGES, "")) || "reference";
3955
+ var sourcesOf = (block) => {
3956
+ const sources = [...block.sources];
3957
+ if (block.spec) {
3958
+ sources.unshift({ spec: block.spec });
3959
+ }
3960
+ return sources;
3961
+ };
3962
+ var referencesFor = (kind, block, defaultLabel, renderer, display) => {
3963
+ if (!block.enabled) {
3964
+ return [];
3965
+ }
3966
+ const sources = sourcesOf(block);
3967
+ const base = normalizeRoute(block.route);
3968
+ return sources.map((source, index) => {
3969
+ const label = source.label ?? (sources.length > 1 ? `${defaultLabel} ${index + 1}` : defaultLabel);
3970
+ let route;
3971
+ if (source.route) {
3972
+ route = normalizeRoute(source.route);
3973
+ } else if (sources.length === 1) {
3974
+ route = base;
3975
+ } else {
3976
+ const suffix = source.label ? slugify(source.label) : "";
3977
+ route = normalizeRoute(`${base}/${suffix || index + 1}`);
3978
+ }
3979
+ return {
3980
+ display,
3981
+ kind,
3982
+ label,
3983
+ renderer,
3984
+ route,
3985
+ slug: routeSlug(route),
3986
+ spec: source.spec,
3987
+ theme: block.theme
3988
+ };
3989
+ });
3990
+ };
3991
+ var NO_DISPLAY = { codeSamples: [], expandSchemas: false };
3992
+ var resolveReferences = (config) => [
3993
+ ...referencesFor("openapi", config.openapi, "API Reference", config.openapi.renderer, {
3994
+ codeSamples: config.openapi.codeSamples,
3995
+ expandSchemas: config.openapi.expandSchemas
3996
+ }),
3997
+ ...referencesFor("asyncapi", config.asyncapi, "Events", "scalar", NO_DISPLAY)
3998
+ ];
3999
+ var referenceTabs = (config) => resolveReferences(config).map((ref) => ({
4000
+ label: ref.label,
4001
+ path: ref.route
4002
+ }));
4003
+ var blumeReferences = (config) => {
4004
+ const seen = new Set;
4005
+ const result = [];
4006
+ for (const ref of resolveReferences(config)) {
4007
+ if (ref.kind !== "openapi" || ref.renderer !== "blume") {
4008
+ continue;
4009
+ }
4010
+ if (seen.has(ref.route)) {
4011
+ continue;
4012
+ }
4013
+ seen.add(ref.route);
4014
+ result.push(ref);
4015
+ }
4016
+ return result;
4017
+ };
4018
+ var hasScalarReferences = (config) => resolveReferences(config).some((ref) => ref.renderer === "scalar");
4019
+
3925
4020
  // src/openapi/scalar.ts
3926
- import { readFile as readFile4 } from "node:fs/promises";
3927
- import { isAbsolute as isAbsolute3, join as join10 } from "pathe";
4021
+ import { readFile as readFile5 } from "node:fs/promises";
4022
+ import { isAbsolute as isAbsolute4, join as join12 } from "pathe";
3928
4023
 
3929
4024
  // 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";
4025
+ import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
4026
+ import { dirname as dirname6, join as join11 } from "pathe";
3932
4027
 
3933
4028
  // src/core/assets.ts
3934
- import { join as join8 } from "pathe";
4029
+ import { join as join10 } from "pathe";
3935
4030
  var resolveAssetMounts = (root, assets) => assets.map((entry) => {
3936
4031
  const rel = entry.replace(/^[./]+/u, "").replaceAll(/\.\.\/?/gu, "").replace(/\/+$/u, "");
3937
- return { dir: join8(root, rel), url: `/${rel}` };
4032
+ return { dir: join10(root, rel), url: `/${rel}` };
3938
4033
  });
3939
4034
 
3940
4035
  // src/theme/fonts.ts
@@ -4093,7 +4188,7 @@ var WORKSPACE_MARKERS = [
4093
4188
  "yarn.lock"
4094
4189
  ];
4095
4190
  var hasWorkspacesField = (pkgPath) => {
4096
- if (!existsSync6(pkgPath)) {
4191
+ if (!existsSync8(pkgPath)) {
4097
4192
  return false;
4098
4193
  }
4099
4194
  try {
@@ -4103,7 +4198,7 @@ var hasWorkspacesField = (pkgPath) => {
4103
4198
  return false;
4104
4199
  }
4105
4200
  };
4106
- var hasWorkspaceMarker = (dir) => hasWorkspacesField(join9(dir, "package.json")) || WORKSPACE_MARKERS.some((marker) => existsSync6(join9(dir, marker)));
4201
+ var hasWorkspaceMarker = (dir) => hasWorkspacesField(join11(dir, "package.json")) || WORKSPACE_MARKERS.some((marker) => existsSync8(join11(dir, marker)));
4107
4202
  var findWorkspaceRoot = (start) => {
4108
4203
  let dir = start;
4109
4204
  for (;; ) {
@@ -4138,7 +4233,7 @@ var runtimeDependencies = (options) => {
4138
4233
  if (needsSvelte) {
4139
4234
  deps.push("@astrojs/svelte");
4140
4235
  }
4141
- if (config.openapi.enabled || config.asyncapi.enabled) {
4236
+ if (hasScalarReferences(config)) {
4142
4237
  deps.push("@scalar/astro");
4143
4238
  }
4144
4239
  deps.push(...searchProviderMeta(config.search.provider).runtimeDeps);
@@ -4171,6 +4266,7 @@ var RENDER_EXTERNAL_DEPS = [
4171
4266
  ];
4172
4267
  var renderUserAliases = (aliases) => Object.entries(aliases ?? {}).toSorted(([a], [b]) => b.length - a.length).map(([find, replacement]) => `
4173
4268
  ${JSON.stringify(find)}: ${JSON.stringify(replacement)},`).join("");
4269
+ var astroOutDir = (context) => context.distDir ?? `${context.root}/dist`;
4174
4270
  var astroConfigTemplate = (options) => {
4175
4271
  const { context, config, needsReact, pages, dataPath, themePath } = options;
4176
4272
  const {
@@ -4178,6 +4274,7 @@ var astroConfigTemplate = (options) => {
4178
4274
  examplesPath,
4179
4275
  needsSvelte,
4180
4276
  needsVue,
4277
+ openapiPath,
4181
4278
  searchClientPath
4182
4279
  } = options;
4183
4280
  const { deployment } = config;
@@ -4248,7 +4345,7 @@ ${twoslashImport}${reactImport}${vueImport}${svelteImport}${blumeImport}${adapte
4248
4345
  export default defineConfig({
4249
4346
  root: ${JSON.stringify(context.outDir)},
4250
4347
  srcDir: ${JSON.stringify(`${context.outDir}/src`)},
4251
- outDir: ${JSON.stringify(`${context.root}/dist`)},
4348
+ outDir: ${JSON.stringify(astroOutDir(context))},
4252
4349
  publicDir: ${JSON.stringify(`${context.root}/public`)},
4253
4350
  output: ${JSON.stringify(deployment.output)},${adapterOption}${siteOption}${baseOption}${redirectsOption}${i18nOption}${fontsOption}
4254
4351
  integrations: [${integrations.join(", ")}],
@@ -4281,6 +4378,7 @@ export default defineConfig({
4281
4378
  alias: {
4282
4379
  "blume:data": ${JSON.stringify(dataPath)},
4283
4380
  "blume:examples": ${JSON.stringify(examplesPath)},
4381
+ "blume:openapi": ${JSON.stringify(openapiPath)},
4284
4382
  "blume:search-client": ${JSON.stringify(searchClientPath)},
4285
4383
  "blume:theme": ${JSON.stringify(themePath)},${userAliasLines}
4286
4384
  },
@@ -4294,7 +4392,7 @@ export default defineConfig({
4294
4392
  });
4295
4393
  `;
4296
4394
  };
4297
- var stagedContentDir = (outDir) => join9(outDir, "content");
4395
+ var stagedContentDir = (outDir) => join11(outDir, "content");
4298
4396
  var contentConfigTemplate = (options) => {
4299
4397
  const { context, config } = options;
4300
4398
  const stagedBase = options.stagedBase ?? stagedContentDir(context.outDir);
@@ -4700,7 +4798,10 @@ import FileTree from "blume/components/content/FileTree.astro";
4700
4798
  import Frame from "blume/components/content/Frame.astro";
4701
4799
  import GithubInfo from "blume/components/content/GithubInfo.astro";
4702
4800
  import Panel from "blume/components/content/Panel.astro";
4801
+ import ParamField from "blume/components/content/ParamField.astro";
4703
4802
  import Prompt from "blume/components/content/Prompt.astro";
4803
+ import RequestField from "blume/components/content/RequestField.astro";
4804
+ import ResponseField from "blume/components/content/ResponseField.astro";
4704
4805
  import Step from "blume/components/content/Step.astro";
4705
4806
  import Steps from "blume/components/content/Steps.astro";
4706
4807
  import Tab from "blume/components/content/Tab.astro";
@@ -4714,6 +4815,8 @@ import TypeTable from "blume/components/content/TypeTable.astro";
4714
4815
  import Visibility from "blume/components/content/Visibility.astro";
4715
4816
  import YouTube from "blume/components/content/YouTube.astro";
4716
4817
  import Icon from "blume/components/Icon.astro";
4818
+ import ApiOverview from "blume/components/openapi/ApiOverview.astro";
4819
+ import Operation from "blume/components/openapi/Operation.astro";
4717
4820
  ${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../generated/components.ts";
4718
4821
  import { islandComponents } from "../generated/islands.ts";
4719
4822
  import data from "../generated/data.json";
@@ -4730,6 +4833,7 @@ export const prerender = true;
4730
4833
  const components = {
4731
4834
  Accordion,
4732
4835
  AccordionItem,
4836
+ ApiOverview,
4733
4837
  AutoTypeTable,
4734
4838
  Badge,
4735
4839
  Callout,
@@ -4747,8 +4851,12 @@ const components = {
4747
4851
  Frame,
4748
4852
  GithubInfo,
4749
4853
  Icon,
4854
+ Operation,
4750
4855
  Panel,
4856
+ ParamField,
4751
4857
  Prompt,
4858
+ RequestField,
4859
+ ResponseField,
4752
4860
  Step,
4753
4861
  Steps,
4754
4862
  Tab,
@@ -5148,6 +5256,11 @@ declare module "blume:data" {
5148
5256
  export default data;
5149
5257
  }
5150
5258
 
5259
+ declare module "blume:openapi" {
5260
+ const specs: import("blume/openapi/model.ts").OpenApiData;
5261
+ export default specs;
5262
+ }
5263
+
5151
5264
  declare module "blume:search-client" {
5152
5265
  export const createSearch: () =>
5153
5266
  | import("blume/components/layout/search/types.ts").SearchFn
@@ -5262,57 +5375,11 @@ ${dark}`;
5262
5375
 
5263
5376
  // src/openapi/scalar.ts
5264
5377
  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
- };
5378
+ var ROUTE_EDGES2 = /^\/+|\/+$/gu;
5276
5379
  var referencePagePath = (route) => {
5277
- const segments = route.replace(ROUTE_EDGES, "");
5380
+ const segments = route.replace(ROUTE_EDGES2, "");
5278
5381
  return `${segments === "" ? "index" : segments}.astro`;
5279
5382
  };
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
5383
  var darkModeConfig = (mode) => {
5317
5384
  if (mode === "dark") {
5318
5385
  return { darkMode: true };
@@ -5337,9 +5404,9 @@ var specConfiguration = async (spec, root) => {
5337
5404
  if (URL_SPEC.test(spec)) {
5338
5405
  return { config: { url: spec } };
5339
5406
  }
5340
- const absolute = isAbsolute3(spec) ? spec : join10(root, spec);
5407
+ const absolute = isAbsolute4(spec) ? spec : join12(root, spec);
5341
5408
  try {
5342
- return { config: { content: await readFile4(absolute, "utf-8") } };
5409
+ return { config: { content: await readFile5(absolute, "utf-8") } };
5343
5410
  } catch {
5344
5411
  return {
5345
5412
  config: { url: spec },
@@ -5353,6 +5420,9 @@ var buildReferenceFiles = async (options) => {
5353
5420
  const seen = new Set;
5354
5421
  const accepted = [];
5355
5422
  for (const ref of resolveReferences(config)) {
5423
+ if (ref.renderer !== "scalar") {
5424
+ continue;
5425
+ }
5356
5426
  if (seen.has(ref.route)) {
5357
5427
  warnings.push(`Two API reference sources resolve to ${ref.route}; keeping the first.`);
5358
5428
  continue;
@@ -5391,6 +5461,284 @@ var buildReferenceFiles = async (options) => {
5391
5461
  return { files, warnings };
5392
5462
  };
5393
5463
 
5464
+ // src/core/sources/cache.ts
5465
+ import { mkdir as mkdir2, readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
5466
+ import { join as join13 } from "pathe";
5467
+ var hashText = (text) => {
5468
+ let hash = 5381;
5469
+ for (let i = 0;i < text.length; i += 1) {
5470
+ hash = (hash * 33 + (text.codePointAt(i) ?? 0)) % 2147483647;
5471
+ }
5472
+ return hash.toString(36);
5473
+ };
5474
+ var entriesDigest = (entries) => hashText(entries.map((entry) => `${entry.ref}:${entry.hash ?? hashText(entry.body.text)}`).join("|"));
5475
+ var pollingWatch = (load2, intervalSeconds) => (onChange) => {
5476
+ let last = "";
5477
+ const tick = async () => {
5478
+ try {
5479
+ const { entries } = await load2();
5480
+ const next = entriesDigest(entries);
5481
+ if (last && next !== last) {
5482
+ onChange();
5483
+ }
5484
+ last = next;
5485
+ } catch {}
5486
+ };
5487
+ const timer = setInterval(() => {
5488
+ tick();
5489
+ }, intervalSeconds * 1000);
5490
+ return () => clearInterval(timer);
5491
+ };
5492
+ var snapshotCache = (cacheDir) => {
5493
+ const file = join13(cacheDir, "entries.json");
5494
+ return {
5495
+ read: async () => {
5496
+ try {
5497
+ return JSON.parse(await readFile6(file, "utf-8"));
5498
+ } catch {
5499
+ return [];
5500
+ }
5501
+ },
5502
+ write: async (entries) => {
5503
+ try {
5504
+ await mkdir2(cacheDir, { recursive: true });
5505
+ await writeFile3(file, `${JSON.stringify(entries)}
5506
+ `, "utf-8");
5507
+ } catch {}
5508
+ }
5509
+ };
5510
+ };
5511
+ var loadWithCache = async (name, cache, fetchEntries, refresh = true) => {
5512
+ if (!refresh) {
5513
+ const cached3 = await cache.read();
5514
+ if (cached3.length > 0) {
5515
+ return { diagnostics: [], entries: cached3 };
5516
+ }
5517
+ }
5518
+ try {
5519
+ const entries = await fetchEntries();
5520
+ await cache.write(entries);
5521
+ return { diagnostics: [], entries };
5522
+ } catch (error) {
5523
+ const fallback = await cache.read();
5524
+ if (fallback.length > 0) {
5525
+ const diagnostic = {
5526
+ code: "BLUME_SOURCE_OFFLINE",
5527
+ message: `Source "${name}" could not be fetched (${error.message}); served ${fallback.length} cached entries.`,
5528
+ severity: "warning"
5529
+ };
5530
+ return { diagnostics: [diagnostic], entries: fallback };
5531
+ }
5532
+ throw new BlumeError({
5533
+ code: "BLUME_SOURCE_FETCH_FAILED",
5534
+ message: `Source "${name}" failed to load and no cache is available: ${error.message}`,
5535
+ severity: "error"
5536
+ });
5537
+ }
5538
+ };
5539
+
5540
+ // src/openapi/model.ts
5541
+ var NON_SLUG2 = /[^a-z0-9]+/gu;
5542
+ var SLUG_EDGES2 = /^-+|-+$/gu;
5543
+ var slugify2 = (text) => text.toLowerCase().replace(NON_SLUG2, "-").replace(SLUG_EDGES2, "");
5544
+ var HTTP_METHODS = [
5545
+ "get",
5546
+ "put",
5547
+ "post",
5548
+ "delete",
5549
+ "options",
5550
+ "head",
5551
+ "patch",
5552
+ "trace"
5553
+ ];
5554
+ var UNTAGGED = "Operations";
5555
+ var operationKey = (method, path, operationId) => {
5556
+ const fromId = operationId ? slugify2(operationId) : "";
5557
+ return fromId || slugify2(`${method}-${path}`);
5558
+ };
5559
+ var isOperation = (value) => typeof value === "object" && value !== null;
5560
+ var extractOperations = (document, baseRoute) => {
5561
+ const operations = [];
5562
+ const tagOrder = [];
5563
+ const tagMeta = new Map((document.tags ?? []).map((tag) => [tag.name, tag.description ?? ""]));
5564
+ const seen = new Set;
5565
+ for (const [path, rawItem] of Object.entries(document.paths ?? {})) {
5566
+ const item = rawItem;
5567
+ if (!item || "$ref" in item) {
5568
+ continue;
5569
+ }
5570
+ for (const method of HTTP_METHODS) {
5571
+ const operation = item[method];
5572
+ if (!isOperation(operation)) {
5573
+ continue;
5574
+ }
5575
+ const tag = operation.tags?.[0] ?? UNTAGGED;
5576
+ const tagSlug = slugify2(tag) || "operations";
5577
+ if (!tagOrder.includes(tag)) {
5578
+ tagOrder.push(tag);
5579
+ }
5580
+ let key = operationKey(method, path, operation.operationId);
5581
+ while (seen.has(key)) {
5582
+ key = `${key}-${method}`;
5583
+ }
5584
+ seen.add(key);
5585
+ operations.push({
5586
+ deprecated: operation.deprecated ?? false,
5587
+ description: operation.description ?? "",
5588
+ key,
5589
+ method,
5590
+ operationId: operation.operationId,
5591
+ path,
5592
+ route: `${baseRoute}/${tagSlug}/${key}`,
5593
+ summary: operation.summary ?? "",
5594
+ tag,
5595
+ tagSlug
5596
+ });
5597
+ }
5598
+ }
5599
+ const tags = tagOrder.map((name) => ({
5600
+ description: tagMeta.get(name) ?? "",
5601
+ name,
5602
+ slug: slugify2(name) || "operations"
5603
+ }));
5604
+ return { operations, tags };
5605
+ };
5606
+
5607
+ // src/openapi/parse.ts
5608
+ import { readFile as readFile7 } from "node:fs/promises";
5609
+ import { normalize as normalize2, upgrade } from "@scalar/openapi-parser";
5610
+ import { isAbsolute as isAbsolute5, join as join14 } from "pathe";
5611
+ var URL_SPEC2 = /^https?:\/\//u;
5612
+ var readSpecText = async (spec, root) => {
5613
+ if (URL_SPEC2.test(spec)) {
5614
+ const response = await fetch(spec);
5615
+ if (!response.ok) {
5616
+ throw new Error(`${spec} -> ${response.status} ${response.statusText}`);
5617
+ }
5618
+ return await response.text();
5619
+ }
5620
+ const absolute = isAbsolute5(spec) ? spec : join14(root, spec);
5621
+ return await readFile7(absolute, "utf-8");
5622
+ };
5623
+ var parseSpec = async (spec, root) => {
5624
+ const text = await readSpecText(spec, root);
5625
+ const normalized = normalize2(text);
5626
+ const { specification } = upgrade(normalized);
5627
+ return { document: specification, warnings: [] };
5628
+ };
5629
+
5630
+ // src/openapi/render-mdx.ts
5631
+ var MDX_UNSAFE = /[<>{}]/gu;
5632
+ var ENTITIES = {
5633
+ "<": "&lt;",
5634
+ ">": "&gt;",
5635
+ "{": "&#123;",
5636
+ "}": "&#125;"
5637
+ };
5638
+ var mdxSafe = (text) => text.replace(MDX_UNSAFE, (char) => ENTITIES[char] ?? char);
5639
+ var withDescription = (description, component) => description.trim() ? `${mdxSafe(description.trim())}
5640
+
5641
+ ${component}` : component;
5642
+ var operationMdx = (spec, operation) => {
5643
+ const method = operation.method.toUpperCase();
5644
+ const title = operation.summary || `${method} ${operation.path}`;
5645
+ const description = operation.description.trim() === operation.summary.trim() ? "" : operation.description;
5646
+ return {
5647
+ body: withDescription(description, `<Operation source="${spec.slug}" id="${operation.key}" />`),
5648
+ data: {
5649
+ ...operation.deprecated ? { deprecated: true } : {},
5650
+ search: { tags: [operation.tag, method] },
5651
+ sidebar: { badge: method, label: operation.summary || operation.path },
5652
+ title,
5653
+ type: "openapi-operation"
5654
+ }
5655
+ };
5656
+ };
5657
+ var overviewMdx = (spec) => ({
5658
+ body: withDescription(spec.description, `<ApiOverview source="${spec.slug}" />`),
5659
+ data: {
5660
+ sidebar: { label: "Overview" },
5661
+ title: spec.title || spec.label
5662
+ }
5663
+ });
5664
+
5665
+ // src/openapi/source.ts
5666
+ var isOpenApiSource = (source) => source.kind === "openapi-source";
5667
+ var routeToRef = (route) => route.replace(/^\/+/u, "");
5668
+ var toEntry = (rendered, ref) => {
5669
+ const raw = frontmatter_default.stringify(`${rendered.body}
5670
+ `, rendered.data);
5671
+ return {
5672
+ body: { format: "mdx", text: rendered.body },
5673
+ data: rendered.data,
5674
+ hash: hashText(raw),
5675
+ raw,
5676
+ ref
5677
+ };
5678
+ };
5679
+ var specEntries = (spec, operations) => {
5680
+ const entries = operations.map((operation) => toEntry(operationMdx(spec, operation), `${routeToRef(operation.route)}.mdx`));
5681
+ entries.push(toEntry(overviewMdx(spec), `${routeToRef(spec.route)}/index.mdx`));
5682
+ return entries;
5683
+ };
5684
+ var openApiSource = (references, ctx) => {
5685
+ let parsed = {};
5686
+ const loadReference = async (reference) => {
5687
+ try {
5688
+ const { document } = await parseSpec(reference.spec, ctx.projectRoot);
5689
+ const { operations, tags } = extractOperations(document, reference.route);
5690
+ const info = document.info ?? { title: reference.label, version: "" };
5691
+ const spec = {
5692
+ codeSamples: reference.display.codeSamples,
5693
+ description: info.description ?? "",
5694
+ document,
5695
+ expandSchemas: reference.display.expandSchemas,
5696
+ label: reference.label,
5697
+ operations: Object.fromEntries(operations.map((operation) => [operation.key, operation])),
5698
+ route: reference.route,
5699
+ slug: reference.slug,
5700
+ tags,
5701
+ title: info.title ?? reference.label,
5702
+ version: info.version ?? ""
5703
+ };
5704
+ return {
5705
+ entries: specEntries(spec, operations),
5706
+ slug: reference.slug,
5707
+ spec
5708
+ };
5709
+ } catch (error) {
5710
+ return {
5711
+ code: "BLUME_OPENAPI_UNAVAILABLE",
5712
+ message: `Could not load OpenAPI spec "${reference.spec}" for ${reference.route} (${error.message}); its reference pages were skipped.`,
5713
+ severity: "warning"
5714
+ };
5715
+ }
5716
+ };
5717
+ const load2 = async () => {
5718
+ const results = await Promise.all(references.map(loadReference));
5719
+ const entries = [];
5720
+ const diagnostics = [];
5721
+ const data = {};
5722
+ for (const result of results) {
5723
+ if ("severity" in result) {
5724
+ diagnostics.push(result);
5725
+ continue;
5726
+ }
5727
+ data[result.slug] = result.spec;
5728
+ entries.push(...result.entries);
5729
+ }
5730
+ parsed = data;
5731
+ return { diagnostics, entries };
5732
+ };
5733
+ return {
5734
+ kind: "openapi-source",
5735
+ load: load2,
5736
+ name: "openapi",
5737
+ openApiData: () => parsed,
5738
+ staged: true
5739
+ };
5740
+ };
5741
+
5394
5742
  // src/theme/entry.ts
5395
5743
  var tailwindEntryTemplate = (options) => `/* Generated by Blume. Do not edit. */
5396
5744
  @import "tailwindcss";
@@ -6011,8 +6359,8 @@ ${options.userTheme}
6011
6359
 
6012
6360
  // src/theme/twoslash.ts
6013
6361
  import { readFileSync as readFileSync5 } from "node:fs";
6014
- import { createRequire as createRequire3 } from "node:module";
6015
- var require2 = createRequire3(import.meta.url);
6362
+ import { createRequire as createRequire4 } from "node:module";
6363
+ var require2 = createRequire4(import.meta.url);
6016
6364
  var OVERRIDES = `
6017
6365
  /* Twoslash: theme the rich renderer with Blume tokens. */
6018
6366
  :root {
@@ -6174,13 +6522,13 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
6174
6522
  };
6175
6523
 
6176
6524
  // src/astro/examples.ts
6177
- import { readFile as readFile6 } from "node:fs/promises";
6178
- import { join as join12, relative as relative4 } from "pathe";
6525
+ import { readFile as readFile9 } from "node:fs/promises";
6526
+ import { join as join16, relative as relative4 } from "pathe";
6179
6527
  import { glob as glob2 } from "tinyglobby";
6180
6528
 
6181
6529
  // src/astro/islands.ts
6182
- import { readFile as readFile5 } from "node:fs/promises";
6183
- import { basename, join as join11 } from "pathe";
6530
+ import { readFile as readFile8 } from "node:fs/promises";
6531
+ import { basename, join as join15 } from "pathe";
6184
6532
  import { glob } from "tinyglobby";
6185
6533
  var DEFAULT_CLIENT = "visible";
6186
6534
  var VALID_MODES = new Set([
@@ -6209,14 +6557,14 @@ var readClientMode = (source, file, warnings) => {
6209
6557
  return mode;
6210
6558
  };
6211
6559
  var discoverIslands = async (root) => {
6212
- const dir = join11(root, "islands");
6560
+ const dir = join15(root, "islands");
6213
6561
  const matches = await glob(["**/*.{jsx,svelte,tsx,vue}"], {
6214
6562
  absolute: true,
6215
6563
  cwd: dir,
6216
6564
  onlyFiles: true
6217
6565
  });
6218
6566
  const files = matches.toSorted();
6219
- const sources = await Promise.all(files.map((file) => readFile5(file, "utf-8")));
6567
+ const sources = await Promise.all(files.map((file) => readFile8(file, "utf-8")));
6220
6568
  const islands = [];
6221
6569
  const warnings = [];
6222
6570
  const seen = new Map;
@@ -6272,14 +6620,14 @@ var splitGlobBase = (pattern) => {
6272
6620
  };
6273
6621
  var discoverExamples = async (root, pattern = "examples") => {
6274
6622
  const { base, rest } = GLOB_MAGIC.test(pattern) ? splitGlobBase(pattern) : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };
6275
- const dir = join12(root, base);
6623
+ const dir = join16(root, base);
6276
6624
  const matches = await glob2([rest], {
6277
6625
  absolute: true,
6278
6626
  cwd: dir,
6279
6627
  onlyFiles: true
6280
6628
  });
6281
6629
  const files = matches.toSorted();
6282
- const sources = await Promise.all(files.map((file) => readFile6(file, "utf-8")));
6630
+ const sources = await Promise.all(files.map((file) => readFile9(file, "utf-8")));
6283
6631
  const examples = [];
6284
6632
  const warnings = [];
6285
6633
  const seen = new Map;
@@ -6353,10 +6701,10 @@ var customOgRoutes = (pages, siteTitle) => {
6353
6701
  };
6354
6702
 
6355
6703
  // src/astro/generate.ts
6356
- var BLUME_SRC = join13(packageRoot(), "src");
6704
+ var BLUME_SRC = join17(packageRoot(), "src");
6357
6705
  var canResolveFrom = (fromDir, spec) => {
6358
6706
  try {
6359
- createRequire4(pathToFileURL2(join13(fromDir, "_.js")).href).resolve(spec);
6707
+ createRequire5(pathToFileURL2(join17(fromDir, "_.js")).href).resolve(spec);
6360
6708
  return true;
6361
6709
  } catch {
6362
6710
  return false;
@@ -6364,15 +6712,15 @@ var canResolveFrom = (fromDir, spec) => {
6364
6712
  };
6365
6713
  var resolvedAstroPath = (fromDir) => {
6366
6714
  try {
6367
- const pkg = createRequire4(pathToFileURL2(join13(fromDir, "_.js")).href).resolve("astro/package.json");
6715
+ const pkg = createRequire5(pathToFileURL2(join17(fromDir, "_.js")).href).resolve("astro/package.json");
6368
6716
  return realpathSync(pkg);
6369
6717
  } catch {
6370
6718
  return null;
6371
6719
  }
6372
6720
  };
6373
6721
  var blumeDepsDir = (pkgDir = packageRoot()) => {
6374
- const candidates = [join13(pkgDir, "node_modules"), dirname7(pkgDir)];
6375
- return candidates.find((dir) => existsSync7(join13(dir, "astro"))) ?? null;
6722
+ const candidates = [join17(pkgDir, "node_modules"), dirname7(pkgDir)];
6723
+ return candidates.find((dir) => existsSync9(join17(dir, "astro"))) ?? null;
6376
6724
  };
6377
6725
  var linkDepsJunction = async (link, depsDir) => {
6378
6726
  const existing = await lstat(link).catch(() => null);
@@ -6382,7 +6730,7 @@ var linkDepsJunction = async (link, depsDir) => {
6382
6730
  }
6383
6731
  await rm(link, { force: true });
6384
6732
  }
6385
- await mkdir2(dirname7(link), { recursive: true });
6733
+ await mkdir3(dirname7(link), { recursive: true });
6386
6734
  await symlink(depsDir, link, "junction");
6387
6735
  };
6388
6736
  var readPkgVersion = (pkgJsonPath) => {
@@ -6412,8 +6760,8 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
6412
6760
  if (blumeAstro && outDirAstro === blumeAstro) {
6413
6761
  return null;
6414
6762
  }
6415
- if (existsSync7(join13(depsDir, "@astrojs", "mdx"))) {
6416
- await linkDepsJunction(join13(outDir, "node_modules"), depsDir);
6763
+ if (existsSync9(join17(depsDir, "@astrojs", "mdx"))) {
6764
+ await linkDepsJunction(join17(outDir, "node_modules"), depsDir);
6417
6765
  return null;
6418
6766
  }
6419
6767
  return astroConflictWarning(blumeAstro, outDirAstro);
@@ -6437,7 +6785,7 @@ var readOptional = async (path) => {
6437
6785
  return "";
6438
6786
  }
6439
6787
  try {
6440
- return await readFile7(path, "utf-8");
6788
+ return await readFile10(path, "utf-8");
6441
6789
  } catch {
6442
6790
  return "";
6443
6791
  }
@@ -6453,16 +6801,16 @@ var detectNeedsReact = async (root) => {
6453
6801
  var writeIfChanged = async (path, content) => {
6454
6802
  let existing = null;
6455
6803
  try {
6456
- existing = await readFile7(path, "utf-8");
6804
+ existing = await readFile10(path, "utf-8");
6457
6805
  } catch {
6458
6806
  existing = null;
6459
6807
  }
6460
6808
  if (existing === content) {
6461
6809
  return false;
6462
6810
  }
6463
- await mkdir2(dirname7(path), { recursive: true });
6811
+ await mkdir3(dirname7(path), { recursive: true });
6464
6812
  const tmp = `${path}.${process.pid}.tmp`;
6465
- await writeFile2(tmp, content, "utf-8");
6813
+ await writeFile4(tmp, content, "utf-8");
6466
6814
  try {
6467
6815
  await rename(tmp, path);
6468
6816
  } catch (error) {
@@ -6477,7 +6825,7 @@ var pruneOrphans = async (srcDir, written) => {
6477
6825
  cwd: srcDir,
6478
6826
  onlyFiles: true
6479
6827
  });
6480
- await Promise.all(existing.map((path) => normalize(path)).filter((path) => !written.has(path)).map((path) => rm(path, { force: true })));
6828
+ await Promise.all(existing.map((path) => normalize3(path)).filter((path) => !written.has(path)).map((path) => rm(path, { force: true })));
6481
6829
  };
6482
6830
  var collectStaged = (project) => {
6483
6831
  const staged = new Map;
@@ -6492,11 +6840,11 @@ var writeStagedContent = async (out, staged) => {
6492
6840
  const contentDir = stagedContentDir(out);
6493
6841
  const written = new Set;
6494
6842
  await Promise.all([...staged].map(async ([entryId, text]) => {
6495
- const path = join13(contentDir, entryId);
6496
- written.add(normalize(path));
6843
+ const path = join17(contentDir, entryId);
6844
+ written.add(normalize3(path));
6497
6845
  await writeIfChanged(path, text);
6498
6846
  }));
6499
- if (existsSync7(contentDir)) {
6847
+ if (existsSync9(contentDir)) {
6500
6848
  await pruneOrphans(contentDir, written);
6501
6849
  }
6502
6850
  };
@@ -6513,9 +6861,9 @@ var resolveLogo = (project) => {
6513
6861
  if (light && light === dark && light.toLowerCase().endsWith(".svg")) {
6514
6862
  const rel = light.replace(/^\//u, "");
6515
6863
  const file = [
6516
- join13(project.context.root, "public", rel),
6517
- join13(project.context.root, rel)
6518
- ].find((path) => existsSync7(path));
6864
+ join17(project.context.root, "public", rel),
6865
+ join17(project.context.root, rel)
6866
+ ].find((path) => existsSync9(path));
6519
6867
  if (file) {
6520
6868
  return { alt, href, svg: readFileSync6(file, "utf-8") };
6521
6869
  }
@@ -6543,7 +6891,7 @@ var faviconType = (name) => {
6543
6891
  };
6544
6892
  var inlineDataUri = (file, type) => `data:${type};base64,${readFileSync6(file).toString("base64")}`;
6545
6893
  var defaultFavicon = () => ({
6546
- href: inlineDataUri(join13(BLUME_SRC, "assets", "icon.png"), "image/png"),
6894
+ href: inlineDataUri(join17(BLUME_SRC, "assets", "icon.png"), "image/png"),
6547
6895
  type: "image/png"
6548
6896
  });
6549
6897
  var APPLE_ICON_CANDIDATES = [
@@ -6555,13 +6903,13 @@ var APPLE_ICON_CANDIDATES = [
6555
6903
  var resolveIconFile = (project, candidates) => {
6556
6904
  const { root } = project.context;
6557
6905
  for (const name of candidates) {
6558
- if (existsSync7(join13(root, "public", name))) {
6906
+ if (existsSync9(join17(root, "public", name))) {
6559
6907
  return { href: `/${name}`, type: faviconType(name) };
6560
6908
  }
6561
6909
  }
6562
6910
  for (const name of candidates) {
6563
- const file = join13(root, name);
6564
- if (existsSync7(file)) {
6911
+ const file = join17(root, name);
6912
+ if (existsSync9(file)) {
6565
6913
  const type = faviconType(name);
6566
6914
  return { href: inlineDataUri(file, type ?? "image/x-icon"), type };
6567
6915
  }
@@ -6620,7 +6968,6 @@ var buildRuntimeData = (project) => {
6620
6968
  chromeVariants: [],
6621
6969
  selectors: [],
6622
6970
  sidebar: [],
6623
- sidebarVariants: [],
6624
6971
  tabs: []
6625
6972
  })
6626
6973
  ])) : {};
@@ -6643,6 +6990,7 @@ var buildRuntimeData = (project) => {
6643
6990
  label
6644
6991
  }))
6645
6992
  } : null,
6993
+ icons: config.icons,
6646
6994
  imageZoom: config.markdown.imageZoom,
6647
6995
  logo: resolveLogo(project),
6648
6996
  mcp: config.mcp.enabled ? { name: config.mcp.name ?? config.title, route: config.mcp.route } : null,
@@ -6689,7 +7037,7 @@ var buildRuntimeData = (project) => {
6689
7037
  var planMcp = (project, srcDir) => {
6690
7038
  const { config } = project;
6691
7039
  const { route } = config.mcp;
6692
- const dir = join13(srcDir, "blume-mcp");
7040
+ const dir = join17(srcDir, "blume-mcp");
6693
7041
  const base = {
6694
7042
  dir,
6695
7043
  discoveryPages: [],
@@ -6713,11 +7061,11 @@ var planMcp = (project, srcDir) => {
6713
7061
  ...base,
6714
7062
  discoveryPages: [
6715
7063
  {
6716
- entrypoint: join13(dir, "discovery.ts"),
7064
+ entrypoint: join17(dir, "discovery.ts"),
6717
7065
  pattern: "/.well-known/mcp.json"
6718
7066
  },
6719
7067
  {
6720
- entrypoint: join13(dir, "server-card.ts"),
7068
+ entrypoint: join17(dir, "server-card.ts"),
6721
7069
  pattern: "/.well-known/mcp/server-card.json"
6722
7070
  }
6723
7071
  ],
@@ -6736,11 +7084,11 @@ var writeMcpFiles = async (project, plan, write) => {
6736
7084
  version: data.version
6737
7085
  };
6738
7086
  await Promise.all([
6739
- write(join13(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
7087
+ write(join17(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
6740
7088
  `),
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)))
7089
+ write(join17(plan.srcDir, "pages", mcpPageFile(plan.route)), mcpEndpointTemplate(plan.route)),
7090
+ write(join17(plan.dir, "discovery.ts"), staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))),
7091
+ write(join17(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
6744
7092
  ]);
6745
7093
  };
6746
7094
  var writeAskFiles = async (project, srcDir, write) => {
@@ -6750,16 +7098,16 @@ var writeAskFiles = async (project, srcDir, write) => {
6750
7098
  }
6751
7099
  const grounded = ask.provider !== "inkeep";
6752
7100
  if (grounded) {
6753
- await write(join13(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
7101
+ await write(join17(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
6754
7102
  `);
6755
7103
  }
6756
- await write(join13(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
7104
+ await write(join17(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
6757
7105
  };
6758
7106
  var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
6759
7107
  if (routeIsTaken(pages, contentPages, "/404")) {
6760
7108
  return;
6761
7109
  }
6762
- await write(join13(srcDir, "pages", "404.astro"), notFoundPageTemplate());
7110
+ await write(join17(srcDir, "pages", "404.astro"), notFoundPageTemplate());
6763
7111
  };
6764
7112
  var shouldGenerateChangelog = (project) => {
6765
7113
  const hasChangelog = project.graph.pages.some((page) => page.contentType === "changelog" && !(page.meta.draft || page.meta.sidebar.hidden));
@@ -6768,7 +7116,7 @@ var shouldGenerateChangelog = (project) => {
6768
7116
  return (hasChangelog || hasChangelogSource) && !changelogRouteTaken;
6769
7117
  };
6770
7118
  var buildComponentSlots = async (componentsFile) => {
6771
- const analysis = componentsFile ? analyzeComponentOverrides(await readFile7(componentsFile, "utf-8"), componentsFile) : null;
7119
+ const analysis = componentsFile ? analyzeComponentOverrides(await readFile10(componentsFile, "utf-8"), componentsFile) : null;
6772
7120
  return {
6773
7121
  plan: planComponentSlots(componentsFile, analysis),
6774
7122
  tags: analysis ? [...analysis.mdx, ...analysis.islands].map((entry) => entry.key) : [],
@@ -6778,14 +7126,15 @@ var buildComponentSlots = async (componentsFile) => {
6778
7126
  var generateRuntime = async (project) => {
6779
7127
  const { context, config } = project;
6780
7128
  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");
7129
+ const srcDir = join17(out, "src");
7130
+ const dataPath = join17(srcDir, "generated", "data.json");
7131
+ const themePath = join17(srcDir, "generated", "app.css");
7132
+ const searchClientPath = join17(srcDir, "generated", "search-client.ts");
7133
+ const examplesPath = join17(srcDir, "generated", "examples.ts");
7134
+ const openapiPath = join17(srcDir, "generated", "openapi.json");
6786
7135
  const written = new Set;
6787
7136
  const write = (path, content) => {
6788
- written.add(normalize(path));
7137
+ written.add(normalize3(path));
6789
7138
  return writeIfChanged(path, content);
6790
7139
  };
6791
7140
  const depsLinkWarning = await ensureDepsLink(out);
@@ -6818,7 +7167,7 @@ var generateRuntime = async (project) => {
6818
7167
  const staged = collectStaged(project);
6819
7168
  const hasStaged = staged.size > 0;
6820
7169
  const structural = await Promise.all([
6821
- write(join13(out, "astro.config.mjs"), astroConfigTemplate({
7170
+ write(join17(out, "astro.config.mjs"), astroConfigTemplate({
6822
7171
  aliases: resolveTsconfigAliases(context.root),
6823
7172
  config,
6824
7173
  contentRoutes: project.manifest.routes.map((route) => route.path),
@@ -6828,24 +7177,25 @@ var generateRuntime = async (project) => {
6828
7177
  needsReact,
6829
7178
  needsSvelte,
6830
7179
  needsVue,
7180
+ openapiPath,
6831
7181
  pages,
6832
7182
  searchClientPath,
6833
7183
  themePath
6834
7184
  })),
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({
7185
+ write(join17(out, "package.json"), runtimePackageTemplate(runtimeDependencies({ config, needsReact, needsSvelte, needsVue }))),
7186
+ write(join17(out, "tsconfig.json"), runtimeTsconfigTemplate()),
7187
+ write(join17(srcDir, "env.d.ts"), envTemplate()),
7188
+ write(join17(srcDir, "content.config.ts"), contentConfigTemplate({ config, context, staged: hasStaged })),
7189
+ write(join17(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
6840
7190
  askEnabled,
6841
7191
  exportEpub,
6842
7192
  exportPdf,
6843
7193
  mathEnabled: config.markdown.math,
6844
7194
  needsReact
6845
7195
  })),
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)),
7196
+ write(join17(srcDir, "generated", "components.ts"), slotPlan.module),
7197
+ write(join17(srcDir, "generated", "islands.ts"), islandMapTemplate(islandDiscovery.islands)),
7198
+ write(join17(srcDir, "generated", "examples.ts"), exampleMapTemplate(exampleDiscovery.examples)),
6849
7199
  write(themePath, tailwindEntryTemplate({
6850
7200
  configTokens: `${buildThemeCss(config.theme)}${buildFontsCss(config.theme.fonts)}`,
6851
7201
  sources: [
@@ -6856,16 +7206,16 @@ var generateRuntime = async (project) => {
6856
7206
  userTheme
6857
7207
  }))
6858
7208
  ]);
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))));
7209
+ await Promise.all(islandDiscovery.islands.map((island) => write(join17(srcDir, "generated", "islands", `${island.name}.astro`), islandWrapperTemplate(island))));
7210
+ await Promise.all(slotPlan.wrappers.map((wrapper) => write(join17(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content)));
7211
+ await Promise.all(exampleDiscovery.examples.map((example) => write(join17(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example))));
6862
7212
  await writeAskFiles(project, srcDir, write);
6863
7213
  await writeMcpFiles(project, mcp, write);
6864
7214
  if (config.seo.og.enabled) {
6865
- await write(join13(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes));
7215
+ await write(join17(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes));
6866
7216
  }
6867
7217
  if (shouldGenerateChangelog(project)) {
6868
- await write(join13(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
7218
+ await write(join17(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
6869
7219
  askEnabled,
6870
7220
  exportEpub,
6871
7221
  exportPdf,
@@ -6877,27 +7227,27 @@ var generateRuntime = async (project) => {
6877
7227
  await write(searchClientPath, searchClientTemplate(config));
6878
7228
  if (servesStaticIndex(config.search.provider)) {
6879
7229
  const documents = await buildSearchDocuments(project);
6880
- await write(join13(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
7230
+ await write(join17(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
6881
7231
  `);
6882
- await write(join13(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
7232
+ await write(join17(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
6883
7233
  }
6884
7234
  if (config.search.provider === "mixedbread") {
6885
- await write(join13(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
7235
+ await write(join17(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
6886
7236
  }
6887
7237
  const rawMarkdown = await buildRawMarkdown(project);
6888
7238
  await Promise.all([
6889
- write(join13(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
7239
+ write(join17(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
6890
7240
  `),
6891
- write(join13(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate()),
6892
- write(join13(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate())
7241
+ write(join17(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate()),
7242
+ write(join17(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate())
6893
7243
  ]);
6894
7244
  const feeds = buildRssFeeds(project);
6895
7245
  if (feeds.length > 0) {
6896
7246
  const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
6897
7247
  await Promise.all([
6898
- write(join13(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
7248
+ write(join17(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
6899
7249
  `),
6900
- write(join13(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
7250
+ write(join17(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
6901
7251
  ]);
6902
7252
  }
6903
7253
  const warnings = [
@@ -6927,17 +7277,20 @@ var generateRuntime = async (project) => {
6927
7277
  }
6928
7278
  }
6929
7279
  warnings.push(...islandFrameworkWarnings(frameworks, context.root));
6930
- if (hasReferences(config)) {
7280
+ if (hasScalarReferences(config)) {
6931
7281
  const references = await buildReferenceFiles({
6932
7282
  config,
6933
7283
  contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
6934
7284
  root: context.root
6935
7285
  });
6936
7286
  warnings.push(...references.warnings);
6937
- await Promise.all(references.files.map((file) => write(join13(srcDir, "pages", file.pagePath), file.content)));
7287
+ await Promise.all(references.files.map((file) => write(join17(srcDir, "pages", file.pagePath), file.content)));
6938
7288
  }
6939
- await write(join13(srcDir, "generated", "data.json"), buildRuntimeData(project));
6940
- await write(join13(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
7289
+ await write(join17(srcDir, "generated", "data.json"), buildRuntimeData(project));
7290
+ const openApiSource2 = project.sources.find(isOpenApiSource);
7291
+ await write(openapiPath, `${JSON.stringify(openApiSource2 ? openApiSource2.openApiData() : {})}
7292
+ `);
7293
+ await write(join17(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
6941
7294
  `);
6942
7295
  await writeStagedContent(out, staged);
6943
7296
  await pruneOrphans(srcDir, written);
@@ -6945,12 +7298,12 @@ var generateRuntime = async (project) => {
6945
7298
  };
6946
7299
 
6947
7300
  // src/core/config.ts
6948
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "node:fs";
7301
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
6949
7302
 
6950
7303
  // 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";
7304
+ import { existsSync as existsSync10 } from "node:fs";
7305
+ import { readFile as readFile12 } from "node:fs/promises";
7306
+ import { join as join18 } from "pathe";
6954
7307
 
6955
7308
  // src/migrate/mintlify/assets.ts
6956
7309
  var assetRefs = (config) => {
@@ -6985,8 +7338,8 @@ var assetSegments = (config) => {
6985
7338
  };
6986
7339
 
6987
7340
  // 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";
7341
+ import { readFile as readFile11 } from "node:fs/promises";
7342
+ import { dirname as dirname8, relative as relative7, resolve as resolve5 } from "pathe";
6990
7343
  var MINTLIFY_DEFAULT_IGNORES = [
6991
7344
  "**/_*",
6992
7345
  "**/.*",
@@ -7027,7 +7380,7 @@ var isInsideRoot = (root, candidate) => {
7027
7380
  };
7028
7381
  var readJsonFile = async (file) => {
7029
7382
  try {
7030
- return JSON.parse(await readFile8(file, "utf-8"));
7383
+ return JSON.parse(await readFile11(file, "utf-8"));
7031
7384
  } catch (error) {
7032
7385
  throw new BlumeError({
7033
7386
  code: "BLUME_MINTLIFY_CONFIG_INVALID",
@@ -7047,7 +7400,7 @@ var resolveRefs = async (value, options) => {
7047
7400
  }
7048
7401
  const ref = asString(object.$ref);
7049
7402
  if (ref) {
7050
- const refFile = resolve4(dirname8(options.file), ref);
7403
+ const refFile = resolve5(dirname8(options.file), ref);
7051
7404
  if (!isInsideRoot(options.root, refFile)) {
7052
7405
  throw new BlumeError({
7053
7406
  code: "BLUME_MINTLIFY_REF_OUTSIDE_ROOT",
@@ -7194,16 +7547,6 @@ var mintlifyBackgroundDecoration = (value) => {
7194
7547
  }
7195
7548
  return;
7196
7549
  };
7197
- var sidebarItemPaths = (items) => items.flatMap((item) => {
7198
- if (typeof item === "string") {
7199
- return [tabPathFromRef(item)];
7200
- }
7201
- if (item.href) {
7202
- return [item.href];
7203
- }
7204
- const paths = item.root ? [tabPathFromRef(item.root)] : [];
7205
- return item.items ? [...paths, ...sidebarItemPaths(item.items)] : paths;
7206
- });
7207
7550
  var collapsedFromExpanded = (value) => {
7208
7551
  if (value === false) {
7209
7552
  return true;
@@ -7275,72 +7618,19 @@ var selectorItemsFor = (items) => items.flatMap((item) => {
7275
7618
  const label = labelForNavItem(object);
7276
7619
  const path = navItemPath(object);
7277
7620
  if (!label || !path) {
7278
- return [];
7279
- }
7280
- return [
7281
- withoutUndefined({
7282
- description: asString(object.description),
7283
- icon: asString(object.icon),
7284
- label,
7285
- path,
7286
- tag: asString(object.tag)
7287
- })
7288
- ];
7289
- });
7290
- var isExternalRoute = (path) => path.startsWith("http://") || path.startsWith("https://") || path.startsWith("mailto:") || path.startsWith("tel:");
7291
- var hasOwnNavigationContent = (item) => Boolean(asString(item.root)) || childItemsFor(item).length > 0;
7292
- var mintlifySidebarVariants = async (spec) => {
7293
- const navigation = asObject(spec.navigation) ?? {};
7294
- const global = asObject(navigation.global) ?? {};
7295
- const candidates = [];
7296
- const addCandidate = (item) => {
7297
- const path = navItemPath(item);
7298
- if (!path || isExternalRoute(path)) {
7299
- return;
7300
- }
7301
- candidates.push({ item, path });
7302
- };
7303
- for (const item of [
7304
- ...asArray(navigation.tabs),
7305
- ...asArray(navigation.anchors),
7306
- ...asArray(global.anchors)
7307
- ]) {
7308
- const object = asObject(item);
7309
- if (!object) {
7310
- addCandidate(item);
7311
- continue;
7312
- }
7313
- for (const menuItem of asArray(object.menu)) {
7314
- addCandidate(menuItem);
7315
- }
7316
- if (hasOwnNavigationContent(object)) {
7317
- addCandidate(item);
7318
- }
7319
- }
7320
- for (const item of [
7321
- ...asArray(navigation.dropdowns),
7322
- ...asArray(navigation.products),
7323
- ...asArray(navigation.versions),
7324
- ...asArray(navigation.languages)
7325
- ]) {
7326
- addCandidate(item);
7327
- }
7328
- const resolved = await Promise.all(candidates.map(async (candidate) => {
7329
- const items = await toSidebarItems([candidate.item], {});
7330
- if (items.length === 0) {
7331
- return [];
7332
- }
7333
- return [candidate.path, ...sidebarItemPaths(items)].filter((path) => !isExternalRoute(path)).map((path) => ({ items, path }));
7334
- }));
7335
- const seen = new Set;
7336
- return resolved.flat().flatMap((variant) => {
7337
- if (!variant || seen.has(variant.path)) {
7338
- return [];
7339
- }
7340
- seen.add(variant.path);
7341
- return [variant];
7342
- });
7343
- };
7621
+ return [];
7622
+ }
7623
+ return [
7624
+ withoutUndefined({
7625
+ description: asString(object.description),
7626
+ icon: asString(object.icon),
7627
+ label,
7628
+ path,
7629
+ tag: asString(object.tag)
7630
+ })
7631
+ ];
7632
+ });
7633
+ var isExternalRoute = (path) => path.startsWith("http://") || path.startsWith("https://") || path.startsWith("mailto:") || path.startsWith("tel:");
7344
7634
  var mintlifyTabs = (spec) => {
7345
7635
  const navigation = asObject(spec.navigation) ?? {};
7346
7636
  const global = asObject(navigation.global) ?? {};
@@ -7403,25 +7693,103 @@ var mintlifySelectors = (spec) => {
7403
7693
  };
7404
7694
  var mintignorePatterns = async (root) => {
7405
7695
  try {
7406
- const raw = await readFile8(resolve4(root, ".mintignore"), "utf-8");
7696
+ const raw = await readFile11(resolve5(root, ".mintignore"), "utf-8");
7407
7697
  return raw.split(`
7408
7698
  `).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).filter((line) => !line.startsWith("!")).map((line) => line.endsWith("/") ? `${line}**` : line);
7409
7699
  } catch {
7410
7700
  return [];
7411
7701
  }
7412
7702
  };
7413
- var mintlifyRedirects = (spec) => asArray(spec.redirects).flatMap((redirect) => {
7414
- const object = asObject(redirect);
7415
- if (!object) {
7416
- return [];
7703
+ var REDIRECT_PARAM = /:(?<name>[A-Za-z_]\w*)(?<modifier>[*+?])?/gu;
7704
+ var toAstroRedirectPath = (path) => path.replaceAll(REDIRECT_PARAM, (_match, name, modifier) => modifier === "*" || modifier === "+" ? `[...${name}]` : `[${name}]`);
7705
+ var isDynamicRedirectPath = (path) => path.includes("[");
7706
+ var partitionMintlifyRedirects = (spec) => {
7707
+ const kept = [];
7708
+ const dropped = [];
7709
+ for (const redirect of asArray(spec.redirects)) {
7710
+ const object = asObject(redirect);
7711
+ if (!object) {
7712
+ continue;
7713
+ }
7714
+ const source = asString(object.source) ?? asString(object.from);
7715
+ const destination = asString(object.destination) ?? asString(object.to) ?? asString(object.redirect);
7716
+ if (!source || !destination) {
7717
+ continue;
7718
+ }
7719
+ const from = toAstroRedirectPath(source);
7720
+ const to = toAstroRedirectPath(destination);
7721
+ if (isDynamicRedirectPath(from) || isDynamicRedirectPath(to)) {
7722
+ dropped.push(source);
7723
+ } else {
7724
+ kept.push({ from, to });
7725
+ }
7726
+ }
7727
+ return { dropped, kept };
7728
+ };
7729
+ var openapiRouteFromDirectory = (value) => {
7730
+ const directory = normalizeDirectory(asString(value) ?? "");
7731
+ return directory.length > 0 ? `/${directory}` : undefined;
7732
+ };
7733
+ var openapiSourcesFromValue = (value, context) => {
7734
+ if (typeof value === "string") {
7735
+ if (value.length === 0 || API_ENDPOINT_REF.test(value)) {
7736
+ return [];
7737
+ }
7738
+ return [
7739
+ withoutUndefined({
7740
+ label: context.label,
7741
+ route: context.route,
7742
+ spec: value
7743
+ })
7744
+ ];
7417
7745
  }
7418
- const from = asString(object.source) ?? asString(object.from);
7419
- const to = asString(object.destination) ?? asString(object.to) ?? asString(object.redirect);
7420
- if (!from || !to) {
7746
+ if (Array.isArray(value)) {
7747
+ return value.flatMap((item) => openapiSourcesFromValue(item, context));
7748
+ }
7749
+ const object = asObject(value);
7750
+ if (!object) {
7421
7751
  return [];
7422
7752
  }
7423
- return [{ from, to }];
7424
- });
7753
+ return openapiSourcesFromValue(object.source ?? object.openapi, {
7754
+ label: context.label,
7755
+ route: openapiRouteFromDirectory(object.directory) ?? context.route
7756
+ });
7757
+ };
7758
+ var mintlifyOpenapi = (spec) => {
7759
+ const drafts = [];
7760
+ const visit = (node) => {
7761
+ if (Array.isArray(node)) {
7762
+ for (const item of node) {
7763
+ visit(item);
7764
+ }
7765
+ return;
7766
+ }
7767
+ const object = asObject(node);
7768
+ if (!object) {
7769
+ return;
7770
+ }
7771
+ if (hasOwn(object, "openapi")) {
7772
+ drafts.push(...openapiSourcesFromValue(object.openapi, {
7773
+ label: labelForNavItem(object)
7774
+ }));
7775
+ }
7776
+ for (const children of childNavigationArrays(object)) {
7777
+ visit(children);
7778
+ }
7779
+ };
7780
+ visit(spec.navigation);
7781
+ drafts.push(...openapiSourcesFromValue(spec.openapi, {}));
7782
+ drafts.push(...openapiSourcesFromValue(asObject(spec.api)?.openapi, {}));
7783
+ const seen = new Set;
7784
+ const sources = drafts.flatMap((draft) => {
7785
+ if (seen.has(draft.spec)) {
7786
+ return [];
7787
+ }
7788
+ seen.add(draft.spec);
7789
+ return [draft];
7790
+ });
7791
+ return sources.length > 0 ? { enabled: true, sources } : undefined;
7792
+ };
7425
7793
  var mintlifyLogo = (value) => {
7426
7794
  if (typeof value === "string") {
7427
7795
  return value;
@@ -7453,6 +7821,12 @@ var mintlifyFavicon = (value) => {
7453
7821
  }
7454
7822
  return withoutUndefined({ dark, light });
7455
7823
  };
7824
+ var mintlifyIcons = (value) => {
7825
+ const library = asString(asObject(value)?.library);
7826
+ return {
7827
+ library: library === "lucide" || library === "tabler" ? library : "fontawesome"
7828
+ };
7829
+ };
7456
7830
  var mintlifyBanner = (value) => {
7457
7831
  const object = asObject(value);
7458
7832
  const content = object ? asString(object.content) : undefined;
@@ -7543,6 +7917,27 @@ var mintlifyMarkdown = (value, styling) => {
7543
7917
  schema: object?.schema === false ? false : undefined
7544
7918
  });
7545
7919
  };
7920
+ var FAMILY_TO_SLUG = Object.fromEntries(Object.entries(GOOGLE_FONTS).map(([slug, def]) => [
7921
+ def.family.toLowerCase(),
7922
+ slug
7923
+ ]));
7924
+ var fontSlugForFamily = (value) => {
7925
+ const family = asString(value);
7926
+ return family ? FAMILY_TO_SLUG[family.toLowerCase()] : undefined;
7927
+ };
7928
+ var mintlifyFonts = (value) => {
7929
+ const object = asObject(value);
7930
+ if (!object) {
7931
+ return;
7932
+ }
7933
+ const heading = asObject(object.heading);
7934
+ const body = asObject(object.body);
7935
+ const fonts = withoutUndefined({
7936
+ body: fontSlugForFamily(body?.family) ?? fontSlugForFamily(object.family),
7937
+ display: fontSlugForFamily(heading?.family) ?? fontSlugForFamily(object.family)
7938
+ });
7939
+ return Object.keys(fonts).length > 0 ? fonts : undefined;
7940
+ };
7546
7941
  var mintlifySeo = (value) => {
7547
7942
  const object = asObject(value);
7548
7943
  const metatags = asObject(object?.metatags);
@@ -7554,8 +7949,8 @@ var mintlifySeo = (value) => {
7554
7949
  };
7555
7950
  };
7556
7951
  var loadMintlifyConfig = async (root, file) => {
7557
- const projectRoot = resolve4(root);
7558
- const configFile = resolve4(file);
7952
+ const projectRoot = resolve5(root);
7953
+ const configFile = resolve5(file);
7559
7954
  const spec = asObject(await resolveRefs(await readJsonFile(configFile), {
7560
7955
  file: configFile,
7561
7956
  root: projectRoot,
@@ -7588,16 +7983,17 @@ var loadMintlifyConfig = async (root, file) => {
7588
7983
  },
7589
7984
  description: asString(spec.description),
7590
7985
  favicon: mintlifyFavicon(spec.favicon),
7986
+ icons: mintlifyIcons(spec.icons),
7591
7987
  logo: mintlifyLogo(spec.logo),
7592
7988
  markdown: mintlifyMarkdown(spec.markdown, styling),
7593
7989
  navigation: {
7594
7990
  chromeVariants: mintlifyChromeVariants(spec),
7595
7991
  selectors: mintlifySelectors(spec),
7596
7992
  sidebar: await toSidebarItems(mintlifyNavigationItems(navigation), {}),
7597
- sidebarVariants: await mintlifySidebarVariants(spec),
7598
7993
  tabs: mintlifyTabs(spec)
7599
7994
  },
7600
- redirects: mintlifyRedirects(spec),
7995
+ openapi: mintlifyOpenapi(spec),
7996
+ redirects: partitionMintlifyRedirects(spec).kept,
7601
7997
  search: {
7602
7998
  indexing: {
7603
7999
  includeHiddenPages: seo.indexing === "all"
@@ -7614,6 +8010,7 @@ var loadMintlifyConfig = async (root, file) => {
7614
8010
  backgroundDecoration: mintlifyBackgroundDecoration(spec.background),
7615
8011
  backgroundImage: backgroundImage.light,
7616
8012
  backgroundImageDark: backgroundImage.dark,
8013
+ fonts: mintlifyFonts(spec.fonts ?? spec.font),
7617
8014
  mode: appearance.default === "light" || appearance.default === "dark" || appearance.default === "system" ? appearance.default : "system",
7618
8015
  strict: appearance.strict === true
7619
8016
  },
@@ -7651,12 +8048,12 @@ var mintlifyI18n = (spec) => {
7651
8048
  // src/core/bridge.ts
7652
8049
  var MINTLIFY_CONFIG_FILES = ["docs.json", "mint.json"];
7653
8050
  var detectMintlifyBridge = async (root) => {
7654
- const configFile = MINTLIFY_CONFIG_FILES.map((name) => join14(root, name)).find((candidate) => existsSync8(candidate));
8051
+ const configFile = MINTLIFY_CONFIG_FILES.map((name) => join18(root, name)).find((candidate) => existsSync10(candidate));
7655
8052
  if (!configFile) {
7656
8053
  return null;
7657
8054
  }
7658
8055
  const config = await loadMintlifyConfig(root, configFile);
7659
- const spec = JSON.parse(await readFile9(configFile, "utf-8"));
8056
+ const spec = JSON.parse(await readFile12(configFile, "utf-8"));
7660
8057
  const i18n = mintlifyI18n(spec);
7661
8058
  if (i18n) {
7662
8059
  config.i18n = i18n;
@@ -7667,7 +8064,7 @@ var detectMintlifyBridge = async (root) => {
7667
8064
  const variables = config.variables ?? {};
7668
8065
  const root_ = config.content?.root ?? ".";
7669
8066
  const exclude = config.content?.exclude ?? [];
7670
- const assets = assetSegments(config).filter((segment) => segment !== "public" && existsSync8(join14(root, segment)));
8067
+ const assets = assetSegments(config).filter((segment) => segment !== "public" && existsSync10(join18(root, segment)));
7671
8068
  return {
7672
8069
  configFile,
7673
8070
  raw: {
@@ -7741,42 +8138,6 @@ var createModuleLoader = () => {
7741
8138
  };
7742
8139
  };
7743
8140
 
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
8141
  // src/core/schema.ts
7781
8142
  import { z as z2 } from "zod";
7782
8143
  var iconName = z2.string().min(1);
@@ -7806,7 +8167,17 @@ var changelogMetaSchema = z2.object({
7806
8167
  date: dateSchema.optional(),
7807
8168
  version: z2.string().optional()
7808
8169
  }).strict();
8170
+ var authorSchema = z2.union([
8171
+ z2.string(),
8172
+ z2.object({
8173
+ avatar: z2.string().optional(),
8174
+ image: z2.string().optional(),
8175
+ name: z2.string(),
8176
+ url: z2.string().optional()
8177
+ }).passthrough()
8178
+ ]);
7809
8179
  var pageMetaBaseSchema = z2.object({
8180
+ authors: z2.union([authorSchema, z2.array(authorSchema)]).optional(),
7810
8181
  changelog: changelogMetaSchema.optional(),
7811
8182
  date: dateSchema.optional(),
7812
8183
  deprecated: z2.boolean().default(false),
@@ -8007,10 +8378,6 @@ var sidebarItemSchema = z2.lazy(() => z2.union([
8007
8378
  root: z2.string().optional()
8008
8379
  }).strict()
8009
8380
  ]));
8010
- var sidebarVariantSchema = z2.object({
8011
- items: z2.array(sidebarItemSchema).default([]),
8012
- path: z2.string()
8013
- }).strict();
8014
8381
  var variablesConfigSchema = z2.record(z2.string().regex(/^[A-Za-z0-9-]+$/u), z2.string()).default({});
8015
8382
  var fontSlug = z2.string().refine(isFontSlug, (value) => ({
8016
8383
  message: `Unknown font "${value}". Supported fonts: ${FONT_SLUGS.join(", ")}.`
@@ -8124,7 +8491,6 @@ var navigationConfigSchema = z2.object({
8124
8491
  repo: z2.boolean().default(true),
8125
8492
  selectors: z2.array(navSelectorSchema).default([]),
8126
8493
  sidebar: z2.array(sidebarItemSchema).optional(),
8127
- sidebarVariants: z2.array(sidebarVariantSchema).default([]),
8128
8494
  tabs: z2.array(navTabSchema).optional()
8129
8495
  }).strict();
8130
8496
  var exportConfigSchema = z2.union([
@@ -8247,7 +8613,10 @@ var openapiSourceSchema = z2.object({
8247
8613
  spec: z2.string()
8248
8614
  }).strict();
8249
8615
  var openapiConfigSchema = z2.object({
8616
+ codeSamples: z2.array(z2.string()).default(["curl", "js", "python"]),
8250
8617
  enabled: z2.boolean().default(false),
8618
+ expandSchemas: z2.boolean().default(false),
8619
+ renderer: z2.enum(["blume", "scalar"]).default("blume"),
8251
8620
  route: z2.string().default("/reference"),
8252
8621
  sources: z2.array(openapiSourceSchema).default([]),
8253
8622
  spec: z2.string().optional(),
@@ -8276,6 +8645,9 @@ var tocConfigSchema = z2.union([
8276
8645
  minLevel: value.minHeadingLevel ?? 2
8277
8646
  };
8278
8647
  });
8648
+ var iconsConfigSchema = z2.object({
8649
+ library: z2.enum(["lucide", "fontawesome", "tabler"]).default("lucide")
8650
+ }).strict();
8279
8651
  var blumeConfigSchema = z2.object({
8280
8652
  ai: aiConfigSchema.default({}),
8281
8653
  analytics: analyticsConfigSchema.optional(),
@@ -8290,6 +8662,7 @@ var blumeConfigSchema = z2.object({
8290
8662
  feedback: z2.boolean().default(true),
8291
8663
  github: githubConfigSchema.optional(),
8292
8664
  i18n: i18nConfigSchema.optional(),
8665
+ icons: iconsConfigSchema.default({}),
8293
8666
  lastModified: lastModifiedConfigSchema.default(false),
8294
8667
  logo: logoConfigSchema.optional(),
8295
8668
  markdown: markdownConfigSchema.default({}),
@@ -8331,7 +8704,7 @@ var loadConfig = async (root, options = {}) => {
8331
8704
  const sourceFile = bridge?.configFile ?? configFile;
8332
8705
  const parsed = blumeConfigSchema.safeParse(raw ?? {});
8333
8706
  if (!parsed.success) {
8334
- const source = sourceFile && existsSync10(sourceFile) ? readFileSync7(sourceFile, "utf-8") : undefined;
8707
+ const source = sourceFile && existsSync11(sourceFile) ? readFileSync7(sourceFile, "utf-8") : undefined;
8335
8708
  const diagnostics = diagnosticsFromZod(parsed.error, {
8336
8709
  code: "BLUME_CONFIG_INVALID",
8337
8710
  file: sourceFile ?? undefined,
@@ -8584,16 +8957,11 @@ var buildNavigation = (pages, options) => {
8584
8957
  options.refByLogical ? page.translationKey : page.route,
8585
8958
  page
8586
8959
  ]));
8587
- const sidebarVariants = (options.sidebarVariants ?? []).map((variant) => ({
8588
- path: variant.path,
8589
- sidebar: buildConfigSidebar(variant.items, byRoute)
8590
- }));
8591
8960
  if (options.sidebar) {
8592
8961
  return {
8593
8962
  chromeVariants,
8594
8963
  selectors,
8595
8964
  sidebar: buildConfigSidebar(options.sidebar, byRoute),
8596
- sidebarVariants,
8597
8965
  tabs
8598
8966
  };
8599
8967
  }
@@ -8601,7 +8969,6 @@ var buildNavigation = (pages, options) => {
8601
8969
  chromeVariants,
8602
8970
  selectors,
8603
8971
  sidebar: buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix),
8604
- sidebarVariants,
8605
8972
  tabs
8606
8973
  };
8607
8974
  };
@@ -8666,7 +9033,6 @@ var buildContentGraph = (pages, options) => {
8666
9033
  selectors: options.navigation.selectors,
8667
9034
  sharedFolderMeta: options.sharedFolderMeta,
8668
9035
  sidebar: options.navigation.sidebar,
8669
- sidebarVariants: options.navigation.sidebarVariants,
8670
9036
  tabs
8671
9037
  });
8672
9038
  }
@@ -8674,7 +9040,6 @@ var buildContentGraph = (pages, options) => {
8674
9040
  chromeVariants: [],
8675
9041
  selectors: [],
8676
9042
  sidebar: [],
8677
- sidebarVariants: [],
8678
9043
  tabs: []
8679
9044
  };
8680
9045
  } else {
@@ -8684,7 +9049,6 @@ var buildContentGraph = (pages, options) => {
8684
9049
  selectors: options.navigation.selectors,
8685
9050
  sharedFolderMeta: options.sharedFolderMeta,
8686
9051
  sidebar: options.navigation.sidebar,
8687
- sidebarVariants: options.navigation.sidebarVariants,
8688
9052
  tabs: options.navigation.tabs
8689
9053
  });
8690
9054
  }
@@ -8808,7 +9172,7 @@ var discoverFolderMeta = async (contentRoot) => {
8808
9172
  };
8809
9173
 
8810
9174
  // src/core/sources/normalize.ts
8811
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
9175
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
8812
9176
  import GithubSlugger from "github-slugger";
8813
9177
  import { extname as extname4 } from "pathe";
8814
9178
  var NUMERIC_PREFIX2 = /^\d+[-_.]/u;
@@ -8816,7 +9180,7 @@ var GROUP_FOLDER2 = /^\((?<label>.+)\)$/u;
8816
9180
  var WORD_SPLIT2 = /[-_]/u;
8817
9181
  var stripNumericPrefix = (segment) => segment.replace(NUMERIC_PREFIX2, "");
8818
9182
  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, "");
9183
+ var slugify3 = (text) => text.toLowerCase().trim().replaceAll(/[^\w\s-]/gu, "").replaceAll(/[\s_]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "");
8820
9184
  var titleCase = (value) => value.split(WORD_SPLIT2).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
8821
9185
  var mapRoute = (relativePath) => {
8822
9186
  const withoutExt = relativePath.slice(0, relativePath.length - extname4(relativePath).length);
@@ -8934,7 +9298,7 @@ var normalizeEntry2 = (entry, ctx) => {
8934
9298
  const ext = format === "mdx" ? ".mdx" : ".md";
8935
9299
  const result = pageMetaSchema.safeParse(entry.data);
8936
9300
  if (!result.success) {
8937
- const source = entry.raw ?? (entry.sourcePath && existsSync11(entry.sourcePath) ? readFileSync8(entry.sourcePath, "utf-8") : undefined);
9301
+ const source = entry.raw ?? (entry.sourcePath && existsSync12(entry.sourcePath) ? readFileSync8(entry.sourcePath, "utf-8") : undefined);
8938
9302
  return {
8939
9303
  diagnostics: diagnosticsFromZod(result.error, {
8940
9304
  code: "BLUME_FRONTMATTER_INVALID",
@@ -8983,15 +9347,15 @@ var normalizeEntry2 = (entry, ctx) => {
8983
9347
  };
8984
9348
 
8985
9349
  // src/core/sources/resolve.ts
8986
- import { join as join24 } from "pathe";
9350
+ import { join as join26 } from "pathe";
8987
9351
 
8988
9352
  // 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";
9353
+ import { existsSync as existsSync13, watch as fsWatch } from "node:fs";
9354
+ import { readFile as readFile13 } from "node:fs/promises";
9355
+ import { extname as extname5, isAbsolute as isAbsolute6, join as join19, relative as relative10, resolve as resolve6 } from "pathe";
8992
9356
  import { glob as glob6 } from "tinyglobby";
8993
9357
  var filesystemSource = (options) => {
8994
- const contentRoot = isAbsolute5(options.root) ? options.root : join16(resolve6(options.projectRoot), options.root);
9358
+ const contentRoot = isAbsolute6(options.root) ? options.root : join19(resolve6(options.projectRoot), options.root);
8995
9359
  const load2 = async () => {
8996
9360
  const files = await glob6(options.include, {
8997
9361
  absolute: true,
@@ -9001,7 +9365,7 @@ var filesystemSource = (options) => {
9001
9365
  });
9002
9366
  files.sort();
9003
9367
  const entries = await Promise.all(files.map(async (file) => {
9004
- const source = await readFile10(file, "utf-8");
9368
+ const source = await readFile13(file, "utf-8");
9005
9369
  const ext = extname5(file).toLowerCase();
9006
9370
  const format = ext === ".mdx" ? "mdx" : "md";
9007
9371
  const parsed = frontmatter_default(source);
@@ -9015,7 +9379,7 @@ var filesystemSource = (options) => {
9015
9379
  return { diagnostics: [], entries };
9016
9380
  };
9017
9381
  const validate = () => {
9018
- if (!existsSync12(contentRoot)) {
9382
+ if (!existsSync13(contentRoot)) {
9019
9383
  throw new BlumeError({
9020
9384
  code: options.missingCode ?? "BLUME_CONTENT_ROOT_MISSING",
9021
9385
  file: contentRoot,
@@ -9026,7 +9390,7 @@ var filesystemSource = (options) => {
9026
9390
  }
9027
9391
  };
9028
9392
  const watch = (onChange) => {
9029
- if (!existsSync12(contentRoot)) {
9393
+ if (!existsSync13(contentRoot)) {
9030
9394
  return () => {};
9031
9395
  }
9032
9396
  const watcher = fsWatch(contentRoot, { recursive: true }, onChange);
@@ -9037,97 +9401,21 @@ var filesystemSource = (options) => {
9037
9401
  load: load2,
9038
9402
  name: options.name,
9039
9403
  prefix: options.prefix,
9040
- read: (ref) => readFile10(join16(contentRoot, ref), "utf-8"),
9404
+ read: (ref) => readFile13(join19(contentRoot, ref), "utf-8"),
9041
9405
  staged: false,
9042
9406
  validate,
9043
9407
  watch
9044
9408
  };
9045
9409
  };
9046
9410
 
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
9411
  // src/core/sources/github-releases.ts
9124
9412
  var DEFAULT_BASE_URL = "https://api.github.com";
9125
9413
  var DEFAULT_LIMIT = 100;
9126
9414
  var PER_PAGE = 100;
9127
9415
  var LEADING_V = /^v/iu;
9128
- var NON_SLUG2 = /[^a-z0-9]+/gu;
9416
+ var NON_SLUG3 = /[^a-z0-9]+/gu;
9129
9417
  var EDGE_DASHES = /^-+|-+$/gu;
9130
- var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG2, "-").replaceAll(EDGE_DASHES, "");
9418
+ var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG3, "-").replaceAll(EDGE_DASHES, "");
9131
9419
  var githubHeaders = () => {
9132
9420
  const headers = new Headers({ Accept: "application/vnd.github+json" });
9133
9421
  const token = process.env.GITHUB_TOKEN;
@@ -9390,25 +9678,25 @@ var mdxRemoteSource = (options, ctx) => {
9390
9678
  };
9391
9679
 
9392
9680
  // 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";
9681
+ import { existsSync as existsSync15, watch as fsWatch2 } from "node:fs";
9682
+ import { readFile as readFile15 } from "node:fs/promises";
9683
+ import { isAbsolute as isAbsolute8, join as join22, relative as relative14, resolve as resolve8 } from "pathe";
9396
9684
  import { glob as glob7 } from "tinyglobby";
9397
9685
 
9398
9686
  // 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";
9687
+ import { existsSync as existsSync14 } from "node:fs";
9688
+ import { readFile as readFile14, writeFile as writeFile5 } from "node:fs/promises";
9689
+ import { isAbsolute as isAbsolute7, join as join20, relative as relative11 } from "pathe";
9402
9690
  var isInsideRoot2 = (root, candidate) => {
9403
9691
  const rel = relative11(root, candidate);
9404
- return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
9692
+ return rel === "" || !rel.startsWith("..") && !isAbsolute7(rel);
9405
9693
  };
9406
9694
  var writeBlumeConfig = async (root, config) => {
9407
9695
  const body = `import { defineConfig } from "blume";
9408
9696
 
9409
9697
  export default defineConfig(${JSON.stringify(config, null, 2)});
9410
9698
  `;
9411
- await writeFile4(join18(root, "blume.config.ts"), body, "utf-8");
9699
+ await writeFile5(join20(root, "blume.config.ts"), body, "utf-8");
9412
9700
  };
9413
9701
  var BLUME_SCRIPTS = {
9414
9702
  build: "blume build",
@@ -9416,13 +9704,13 @@ var BLUME_SCRIPTS = {
9416
9704
  start: "blume preview"
9417
9705
  };
9418
9706
  var rewriteFrameworkScripts = async (root, cli, remove) => {
9419
- const pkgPath = join18(root, "package.json");
9420
- if (!existsSync13(pkgPath)) {
9707
+ const pkgPath = join20(root, "package.json");
9708
+ if (!existsSync14(pkgPath)) {
9421
9709
  return false;
9422
9710
  }
9423
9711
  let pkg;
9424
9712
  try {
9425
- pkg = JSON.parse(await readFile12(pkgPath, "utf-8"));
9713
+ pkg = JSON.parse(await readFile14(pkgPath, "utf-8"));
9426
9714
  } catch {
9427
9715
  return false;
9428
9716
  }
@@ -9445,12 +9733,12 @@ var rewriteFrameworkScripts = async (root, cli, remove) => {
9445
9733
  }
9446
9734
  if (changed) {
9447
9735
  pkg.scripts = next;
9448
- await writeFile4(pkgPath, `${JSON.stringify(pkg, null, 2)}
9736
+ await writeFile5(pkgPath, `${JSON.stringify(pkg, null, 2)}
9449
9737
  `, "utf-8");
9450
9738
  }
9451
9739
  return changed;
9452
9740
  };
9453
- var leftoverFiles = (root, candidates) => candidates.filter((candidate) => existsSync13(join18(root, candidate)));
9741
+ var leftoverFiles = (root, candidates) => candidates.filter((candidate) => existsSync14(join20(root, candidate)));
9454
9742
  var attribute = (attrs, name) => {
9455
9743
  const match = attrs.match(new RegExp(`\\b${name}=(?:"(?<dq>[^"]*)"|'(?<sq>[^']*)')`, "u"));
9456
9744
  return match?.groups?.dq ?? match?.groups?.sq;
@@ -9847,7 +10135,7 @@ var isLiteralObject = (value) => typeof value === "object" && value !== null &&
9847
10135
  var asLiteralArray = (value) => Array.isArray(value) ? value : undefined;
9848
10136
 
9849
10137
  // src/migrate/mintlify/content.ts
9850
- import { dirname as dirname10, join as join19, relative as relative12 } from "pathe";
10138
+ import { dirname as dirname10, join as join21, relative as relative12 } from "pathe";
9851
10139
  var CALLOUT_DIRECTIVES = {
9852
10140
  Check: "success",
9853
10141
  Danger: "danger",
@@ -9876,6 +10164,7 @@ var rewriteMintlifyCallouts = (source) => rewriteCallouts(source, {
9876
10164
  typeDirectives: CALLOUT_TYPE_DIRECTIVES
9877
10165
  });
9878
10166
  var rewriteMintlifyExampleBlocks = (source) => source.replaceAll(/<(?<close>\/?)(?:Request|Response)Example\b/gu, "<$<close>CodeGroup");
10167
+ var rewriteMintlifyAccordions = (source) => source.replaceAll(/<(?<close>\/?)Accordion(?<group>Group)?\b/gu, (_match, close, group) => group ? `<${close}Accordion` : `<${close}AccordionItem`);
9879
10168
  var SNIPPET_IMPORT = /^import\s+[\s\S]*?\s+from\s+["'](?<source>\/snippets\/[^"']+)["'];?[ \t]*\n?/gmu;
9880
10169
  var rewriteSnippetImports = (source, options) => {
9881
10170
  const components = [];
@@ -9883,7 +10172,7 @@ var rewriteSnippetImports = (source, options) => {
9883
10172
  if (/\.mdx?$/u.test(importSource)) {
9884
10173
  return "";
9885
10174
  }
9886
- const target = join19(options.root, importSource.replace(/^\/+/u, ""));
10175
+ const target = join21(options.root, importSource.replace(/^\/+/u, ""));
9887
10176
  components.push(importSource.replace(/^\/+/u, ""));
9888
10177
  let rel = relative12(dirname10(options.filePath), target);
9889
10178
  if (!rel.startsWith(".")) {
@@ -9893,7 +10182,7 @@ var rewriteSnippetImports = (source, options) => {
9893
10182
  });
9894
10183
  return { components, source: next };
9895
10184
  };
9896
- var UNSUPPORTED_COMPONENTS = ["ParamField", "ResponseField"];
10185
+ var UNSUPPORTED_COMPONENTS = ["Update"];
9897
10186
  var unsupportedMintlifyComponents = (source) => UNSUPPORTED_COMPONENTS.filter((name) => new RegExp(`<${name}\\b`, "u").test(source));
9898
10187
 
9899
10188
  // src/migrate/mintlify/frontmatter.ts
@@ -10164,9 +10453,9 @@ var rewriteMintlifyMarkdownSnippets = async (source, options) => {
10164
10453
  throw new Error(snippetCycleMessage(options.root, file, options.trail ?? []));
10165
10454
  }
10166
10455
  seen.add(file);
10167
- const readFile13 = options.readFile ?? readFileFromDisk;
10456
+ const readFile15 = options.readFile ?? readFileFromDisk;
10168
10457
  try {
10169
- const raw = await readFile13(file);
10458
+ const raw = await readFile15(file);
10170
10459
  const content = frontmatter_default(raw).content.trim();
10171
10460
  const transformed = await rewriteMintlifyMarkdownSnippets(content, {
10172
10461
  ...options,
@@ -10207,7 +10496,7 @@ var rewriteMintlifyMarkdownSnippets = async (source, options) => {
10207
10496
  return await inlineAt(0, source);
10208
10497
  };
10209
10498
  var rewriteMintlifySnippetVariables = async (source, options) => {
10210
- const readFile13 = options.readFile ?? readFileFromDisk;
10499
+ const readFile15 = options.readFile ?? readFileFromDisk;
10211
10500
  const inlineImport = async (current, variableImport) => {
10212
10501
  const file = resolveSnippetPath({
10213
10502
  filePath: options.filePath,
@@ -10217,7 +10506,7 @@ var rewriteMintlifySnippetVariables = async (source, options) => {
10217
10506
  if (!file) {
10218
10507
  return current;
10219
10508
  }
10220
- const exports = collectStringExports(frontmatter_default(await readFile13(file)).content);
10509
+ const exports = collectStringExports(frontmatter_default(await readFile15(file)).content);
10221
10510
  let next = current;
10222
10511
  for (const name of variableImport.names) {
10223
10512
  const value = exports.get(name.imported);
@@ -10271,6 +10560,7 @@ var transformMintlifyContent = async (raw, options) => {
10271
10560
  text = snippetImports.source;
10272
10561
  text = rewriteMintlifySvgIconProps(text);
10273
10562
  text = rewriteMintlifyExampleBlocks(text);
10563
+ text = rewriteMintlifyAccordions(text);
10274
10564
  text = rewriteMintlifyCallouts(text);
10275
10565
  const unsupported = unsupportedMintlifyComponents(text);
10276
10566
  const parsed = frontmatter_default(text);
@@ -10295,7 +10585,7 @@ var MINTLIFY_SOURCE_IGNORES = [
10295
10585
  "snippets/**"
10296
10586
  ];
10297
10587
  var mintlifySource = (options) => {
10298
- const contentRoot = isAbsolute7(options.root) ? options.root : join20(resolve8(options.projectRoot), options.root);
10588
+ const contentRoot = isAbsolute8(options.root) ? options.root : join22(resolve8(options.projectRoot), options.root);
10299
10589
  const ignore = [...new Set([...options.exclude, ...MINTLIFY_SOURCE_IGNORES])];
10300
10590
  const transform = (raw, file) => transformMintlifyContent(raw, {
10301
10591
  filePath: file,
@@ -10312,7 +10602,7 @@ var mintlifySource = (options) => {
10312
10602
  files.sort();
10313
10603
  const unsupported = new Set;
10314
10604
  const entries = await Promise.all(files.map(async (file) => {
10315
- const result = await transform(await readFile13(file, "utf-8"), file);
10605
+ const result = await transform(await readFile15(file, "utf-8"), file);
10316
10606
  for (const name of result.unsupported) {
10317
10607
  unsupported.add(name);
10318
10608
  }
@@ -10328,14 +10618,14 @@ var mintlifySource = (options) => {
10328
10618
  const diagnostics = unsupported.size > 0 ? [
10329
10619
  {
10330
10620
  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.`,
10621
+ message: `Mintlify components without a Blume equivalent were left as-is: ${[...unsupported].toSorted().join(", ")}. Replace them by hand or provide a matching component.`,
10332
10622
  severity: "warning"
10333
10623
  }
10334
10624
  ] : [];
10335
10625
  return { diagnostics, entries };
10336
10626
  };
10337
10627
  const validate = () => {
10338
- if (!existsSync14(contentRoot)) {
10628
+ if (!existsSync15(contentRoot)) {
10339
10629
  throw new BlumeError({
10340
10630
  code: "BLUME_CONTENT_ROOT_MISSING",
10341
10631
  file: contentRoot,
@@ -10347,11 +10637,11 @@ var mintlifySource = (options) => {
10347
10637
  };
10348
10638
  const watch = (onChange) => {
10349
10639
  const disposers = [];
10350
- if (existsSync14(contentRoot)) {
10640
+ if (existsSync15(contentRoot)) {
10351
10641
  const watcher = fsWatch2(contentRoot, { recursive: true }, onChange);
10352
10642
  disposers.push(() => watcher.close());
10353
10643
  }
10354
- if (options.configFile && existsSync14(options.configFile)) {
10644
+ if (options.configFile && existsSync15(options.configFile)) {
10355
10645
  const watcher = fsWatch2(options.configFile, onChange);
10356
10646
  disposers.push(() => watcher.close());
10357
10647
  }
@@ -10362,8 +10652,8 @@ var mintlifySource = (options) => {
10362
10652
  };
10363
10653
  };
10364
10654
  const read = async (ref) => {
10365
- const file = join20(contentRoot, ref);
10366
- const result = await transform(await readFile13(file, "utf-8"), file);
10655
+ const file = join22(contentRoot, ref);
10656
+ const result = await transform(await readFile15(file, "utf-8"), file);
10367
10657
  return result.content;
10368
10658
  };
10369
10659
  return {
@@ -10380,11 +10670,11 @@ var mintlifySource = (options) => {
10380
10670
 
10381
10671
  // src/core/sources/notion.ts
10382
10672
  import { setTimeout as sleep } from "node:timers/promises";
10383
- import { join as join22 } from "pathe";
10673
+ import { join as join24 } from "pathe";
10384
10674
 
10385
10675
  // 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";
10676
+ import { mkdir as mkdir4, writeFile as writeFile6 } from "node:fs/promises";
10677
+ import { extname as extname6, join as join23 } from "pathe";
10388
10678
  var MD_IMAGE = /!\[(?<alt>[^\]]*)\]\((?<url>[^)\s]+)\)/gu;
10389
10679
  var REMOTE = /^https?:\/\//u;
10390
10680
  var SAFE_EXT = /^\.[a-z0-9]+$/iu;
@@ -10413,7 +10703,7 @@ var materializeAssets = async (markdown, ctx) => {
10413
10703
  const bytes = new Uint8Array(await res.arrayBuffer());
10414
10704
  const file = `${hashText(url)}${extFor(url)}`;
10415
10705
  await mkdir4(ctx.assetsDir, { recursive: true });
10416
- await writeFile5(join21(ctx.assetsDir, file), bytes);
10706
+ await writeFile6(join23(ctx.assetsDir, file), bytes);
10417
10707
  rewrites.set(url, `${ctx.assetsBaseUrl}/${file}`);
10418
10708
  } catch (error) {
10419
10709
  diagnostics.push({
@@ -10529,8 +10819,8 @@ ${text}
10529
10819
  };
10530
10820
  var notionSource = (options, ctx) => {
10531
10821
  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);
10822
+ const cache = snapshotCache(ctx?.cacheDir ?? join24(".blume", "cache", options.name));
10823
+ const assetsDir = ctx?.assetsDir ?? join24(".blume", "public", "blume-assets", options.name);
10534
10824
  const assetsBaseUrl = ctx?.assetsBaseUrl ?? `/blume-assets/${options.name}`;
10535
10825
  let snapshot = new Map;
10536
10826
  const resolveClient = async () => {
@@ -10635,10 +10925,10 @@ ${rendered.join(`
10635
10925
  data.sidebar = { order };
10636
10926
  }
10637
10927
  const slugProp = richToMarkdown(page.properties[props.slug ?? "Slug"]?.rich_text);
10638
- const slug = slugify2(slugProp || title) || page.id;
10928
+ const slug = slugify3(slugProp || title) || page.id;
10639
10929
  return { data, slug };
10640
10930
  };
10641
- const toEntry = async (client, page) => {
10931
+ const toEntry2 = async (client, page) => {
10642
10932
  const { data, slug } = frontmatter(page);
10643
10933
  const mdx = await renderBlocks(client, await childrenOf(client, page.id));
10644
10934
  const assets = await materializeAssets(mdx, {
@@ -10667,7 +10957,7 @@ ${rendered.join(`
10667
10957
  database_id: options.database,
10668
10958
  start_cursor: cursor
10669
10959
  })));
10670
- const built = await Promise.all(pages.map((page) => toEntry(client, page)));
10960
+ const built = await Promise.all(pages.map((page) => toEntry2(client, page)));
10671
10961
  for (const item of built) {
10672
10962
  assetDiagnostics.push(...item.diagnostics);
10673
10963
  }
@@ -10698,7 +10988,7 @@ ${rendered.join(`
10698
10988
  };
10699
10989
 
10700
10990
  // src/core/sources/sanity.ts
10701
- import { join as join23 } from "pathe";
10991
+ import { join as join25 } from "pathe";
10702
10992
 
10703
10993
  // src/core/sources/portable-text.ts
10704
10994
  var HEADING_STYLES = {
@@ -10837,11 +11127,11 @@ var resolveClient = async (options, preview) => {
10837
11127
  };
10838
11128
  var sanitySource = (options, ctx) => {
10839
11129
  const fields = options.fields ?? {};
10840
- const cache = snapshotCache(ctx?.cacheDir ?? join23(".blume", "cache", options.name));
11130
+ const cache = snapshotCache(ctx?.cacheDir ?? join25(".blume", "cache", options.name));
10841
11131
  let snapshot = new Map;
10842
- const toEntry = (doc) => {
11132
+ const toEntry2 = (doc) => {
10843
11133
  const slugValue = asString2(getPath(doc, fields.slug ?? "slug.current")) ?? asString2(doc._id) ?? "untitled";
10844
- const slug = slugify2(slugValue) || slugify2(asString2(doc._id) ?? "") || "untitled";
11134
+ const slug = slugify3(slugValue) || slugify3(asString2(doc._id) ?? "") || "untitled";
10845
11135
  const data = {};
10846
11136
  const title = asString2(getPath(doc, fields.title ?? "title"));
10847
11137
  const description = asString2(getPath(doc, fields.description ?? "description"));
@@ -10873,7 +11163,7 @@ var sanitySource = (options, ctx) => {
10873
11163
  const result = await loadWithCache(options.name, cache, async () => {
10874
11164
  const client = await resolveClient(options, ctx?.preview ?? false);
10875
11165
  const docs = await client.fetch(options.query);
10876
- return docs.map(toEntry);
11166
+ return docs.map(toEntry2);
10877
11167
  }, ctx?.refresh ?? true);
10878
11168
  snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
10879
11169
  return result;
@@ -10912,8 +11202,8 @@ var uniqueNamer = () => {
10912
11202
  };
10913
11203
  var sourceContext = (context, name, runtime) => ({
10914
11204
  assetsBaseUrl: `/blume-assets/${name}`,
10915
- assetsDir: join24(context.outDir, "public", "blume-assets", name),
10916
- cacheDir: join24(context.outDir, "cache", name),
11205
+ assetsDir: join26(context.outDir, "public", "blume-assets", name),
11206
+ cacheDir: join26(context.outDir, "cache", name),
10917
11207
  mode: runtime.mode,
10918
11208
  preview: runtime.preview,
10919
11209
  projectRoot: context.root,
@@ -10995,7 +11285,7 @@ var baseName = (def) => {
10995
11285
  }
10996
11286
  return def.prefix ?? def.type;
10997
11287
  };
10998
- var resolveSources = (config, context, runtime) => {
11288
+ var contentSources = (config, context, runtime) => {
10999
11289
  const defs = config.content.sources;
11000
11290
  if (!defs || defs.length === 0) {
11001
11291
  return [
@@ -11011,6 +11301,14 @@ var resolveSources = (config, context, runtime) => {
11011
11301
  const nameFor = uniqueNamer();
11012
11302
  return defs.map((def) => buildSource(def, nameFor(baseName(def)), context, runtime));
11013
11303
  };
11304
+ var resolveSources = (config, context, runtime) => {
11305
+ const sources = contentSources(config, context, runtime);
11306
+ const references = blumeReferences(config);
11307
+ if (references.length > 0) {
11308
+ sources.push(openApiSource(references, sourceContext(context, "openapi", runtime)));
11309
+ }
11310
+ return sources;
11311
+ };
11014
11312
 
11015
11313
  // src/core/project-graph.ts
11016
11314
  var applyConfigOverrides = (config, overrides) => {
@@ -11039,7 +11337,9 @@ var scanProject = async (root, options = {}) => {
11039
11337
  });
11040
11338
  const { bridge } = configResult;
11041
11339
  const config = applyConfigOverrides(configResult.config, options.overrides);
11042
- const context = resolveProjectContext(root, config);
11340
+ const context = resolveProjectContext(root, config, {
11341
+ runtimeDir: options.runtimeDir
11342
+ });
11043
11343
  const sources = resolveSources(config, context, {
11044
11344
  mode,
11045
11345
  preview,
@@ -11107,8 +11407,8 @@ var scanProject = async (root, options = {}) => {
11107
11407
  };
11108
11408
 
11109
11409
  // 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";
11410
+ import { existsSync as existsSync16, readFileSync as readFileSync9 } from "node:fs";
11411
+ import { dirname as dirname12, join as join27, resolve as resolve9 } from "pathe";
11112
11412
  var ENV_LINE = /^\s*(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?<value>.*?)\s*$/u;
11113
11413
  var DOUBLE_QUOTED2 = /^"(?<body>[\s\S]*)"$/u;
11114
11414
  var SINGLE_QUOTED = /^'(?<body>[\s\S]*)'$/u;
@@ -11146,7 +11446,7 @@ var applyEnv = (parsed) => {
11146
11446
  };
11147
11447
  var loadFile = (path) => {
11148
11448
  try {
11149
- if (existsSync15(path)) {
11449
+ if (existsSync16(path)) {
11150
11450
  applyEnv(parseEnv(readFileSync9(path, "utf-8")));
11151
11451
  }
11152
11452
  } catch {}
@@ -11155,10 +11455,10 @@ var loadEnvFiles = (startDir) => {
11155
11455
  let dir = resolve9(startDir);
11156
11456
  let done = false;
11157
11457
  while (!done) {
11158
- loadFile(join25(dir, ".env.local"));
11159
- loadFile(join25(dir, ".env"));
11458
+ loadFile(join27(dir, ".env.local"));
11459
+ loadFile(join27(dir, ".env"));
11160
11460
  const parent = dirname12(dir);
11161
- done = existsSync15(join25(dir, ".git")) || parent === dir;
11461
+ done = existsSync16(join27(dir, ".git")) || parent === dir;
11162
11462
  dir = parent;
11163
11463
  }
11164
11464
  };
@@ -11231,7 +11531,8 @@ var prepareProject = async (options) => {
11231
11531
  mode: options.mode,
11232
11532
  overrides: options.overrides,
11233
11533
  preview: options.preview,
11234
- refresh: options.refresh
11534
+ refresh: options.refresh,
11535
+ runtimeDir: options.runtimeDir
11235
11536
  });
11236
11537
  } catch (error) {
11237
11538
  if (error instanceof BlumeError) {
@@ -11287,24 +11588,24 @@ var emitRedirectFiles = async (config, distDir) => {
11287
11588
  if (redirects.length === 0 || config.deployment.output !== "static") {
11288
11589
  return;
11289
11590
  }
11290
- await writeFile6(join26(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
11591
+ await writeFile7(join28(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
11291
11592
  const platformFiles = [
11292
11593
  { content: buildNetlifyRedirects(redirects), name: "_redirects" },
11293
11594
  { content: buildVercelConfig(redirects), name: "vercel.json" }
11294
11595
  ];
11295
- await Promise.all(platformFiles.map((file) => existsSync16(join26(distDir, file.name)) ? Promise.resolve() : writeFile6(join26(distDir, file.name), file.content, "utf-8")));
11596
+ await Promise.all(platformFiles.map((file) => existsSync17(join28(distDir, file.name)) ? Promise.resolve() : writeFile7(join28(distDir, file.name), file.content, "utf-8")));
11296
11597
  logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
11297
11598
  };
11298
11599
  var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${(bytes / 1024).toFixed(bytes < 1024 * 100 ? 1 : 0)} kB`;
11299
11600
  var astroAssets = async (distDir, ext) => {
11300
- const astroDir = join26(distDir, "_astro");
11301
- if (!existsSync16(astroDir)) {
11601
+ const astroDir = join28(distDir, "_astro");
11602
+ if (!existsSync17(astroDir)) {
11302
11603
  return [];
11303
11604
  }
11304
11605
  const entries = await readdir(astroDir);
11305
11606
  const files = entries.filter((name) => name.endsWith(`.${ext}`));
11306
11607
  const sized = await Promise.all(files.map(async (name) => {
11307
- const info = await stat(join26(astroDir, name));
11608
+ const info = await stat(join28(astroDir, name));
11308
11609
  return { name, size: info.size };
11309
11610
  }));
11310
11611
  return sized.toSorted((a, b) => b.size - a.size);
@@ -11346,6 +11647,58 @@ var enforceBudget = async (distDir, args) => {
11346
11647
  }
11347
11648
  return passed ? "pass" : "fail";
11348
11649
  };
11650
+ var publishBuildArtifacts = async (project, distDir, args) => {
11651
+ if (project.config.search.provider === "pagefind") {
11652
+ logger.start("Building search index");
11653
+ const indexed = await buildSearchIndex(distDir);
11654
+ logger.success(`Indexed ${indexed} page(s) for search`);
11655
+ }
11656
+ await syncSearchProvider(project, {
11657
+ start: (message) => logger.start(message),
11658
+ success: (message) => logger.success(message),
11659
+ warn: (message) => logger.warn(message)
11660
+ });
11661
+ if (project.config.ai.llmsTxt) {
11662
+ const { index, full } = await buildLlmsFiles(project);
11663
+ await Promise.all([
11664
+ writeFile7(join28(distDir, "llms.txt"), index, "utf-8"),
11665
+ writeFile7(join28(distDir, "llms-full.txt"), full, "utf-8")
11666
+ ]);
11667
+ logger.success("Generated llms.txt and llms-full.txt");
11668
+ }
11669
+ const sitemap = buildSitemap(project);
11670
+ if (sitemap && !existsSync17(join28(distDir, "sitemap.xml"))) {
11671
+ await writeFile7(join28(distDir, "sitemap.xml"), sitemap, "utf-8");
11672
+ logger.success("Generated sitemap.xml");
11673
+ }
11674
+ const robots = buildRobots(project);
11675
+ if (robots && !existsSync17(join28(distDir, "robots.txt"))) {
11676
+ await writeFile7(join28(distDir, "robots.txt"), robots, "utf-8");
11677
+ logger.success("Generated robots.txt");
11678
+ }
11679
+ await emitRedirectFiles(project.config, distDir);
11680
+ const { config } = project;
11681
+ const features = serverFeatures(config);
11682
+ logger.box([
11683
+ `Output ${config.deployment.output}`,
11684
+ `Adapter ${config.deployment.adapter ?? "none"}`,
11685
+ `Site ${config.deployment.site ?? "not set"}`,
11686
+ `Search ${config.search.provider}`,
11687
+ `Redirects ${config.redirects.length}`,
11688
+ `Sitemap ${sitemap ? "yes" : "no (set deployment.site)"}`,
11689
+ `Robots ${robots ? "yes" : "no"}`,
11690
+ `LLM files ${config.ai.llmsTxt ? "yes" : "no"}`,
11691
+ `Server features ${features.length > 0 ? features.join(", ") : "none"}`
11692
+ ].join(`
11693
+ `));
11694
+ if (args.analyze) {
11695
+ await reportBundleSizes(distDir);
11696
+ }
11697
+ if (await enforceBudget(distDir, args) === "fail") {
11698
+ process.exit(1);
11699
+ }
11700
+ logger.success(`Built to ${distDir}`);
11701
+ };
11349
11702
  var buildCommand = defineCommand2({
11350
11703
  args: {
11351
11704
  adapter: {
@@ -11368,6 +11721,10 @@ var buildCommand = defineCommand2({
11368
11721
  description: "Fail if total client JavaScript exceeds this many kB.",
11369
11722
  type: "string"
11370
11723
  },
11724
+ isolated: {
11725
+ 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.",
11726
+ type: "boolean"
11727
+ },
11371
11728
  output: {
11372
11729
  description: "Output mode: static | server.",
11373
11730
  type: "string"
@@ -11384,7 +11741,11 @@ var buildCommand = defineCommand2({
11384
11741
  },
11385
11742
  async run({ args }) {
11386
11743
  const root = process.cwd();
11387
- refuseIfDevRunning(root, "building");
11744
+ const runtimeDir = args.isolated ? ".blume-verify" : process.env.BLUME_RUNTIME_DIR;
11745
+ refuseIfDevRunning(root, "building", runtimeDir);
11746
+ if (args.isolated) {
11747
+ await ensureGitignore(root, [".blume-verify/"]);
11748
+ }
11388
11749
  if (args.output && args.output !== "static" && args.output !== "server") {
11389
11750
  logger.error(`Invalid --output "${args.output}" (use static | server).`);
11390
11751
  process.exit(1);
@@ -11403,6 +11764,7 @@ var buildCommand = defineCommand2({
11403
11764
  },
11404
11765
  preview: args.preview,
11405
11766
  root,
11767
+ runtimeDir,
11406
11768
  strict: args.strict
11407
11769
  });
11408
11770
  logger.start(`Building ${project.graph.pages.length} page(s) (${project.config.deployment.output} output)`);
@@ -11410,68 +11772,27 @@ var buildCommand = defineCommand2({
11410
11772
  logLevel: "info",
11411
11773
  root: project.context.outDir
11412
11774
  });
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);
11775
+ const distDir = project.context.distDir ?? join28(root, "dist");
11776
+ if (runtimeDir) {
11777
+ logger.success(`Isolated build OK — output at ${distDir} (not published).`);
11778
+ return;
11462
11779
  }
11463
- logger.success(`Built to ${distDir}`);
11780
+ await publishBuildArtifacts(project, distDir, args);
11464
11781
  }
11465
11782
  });
11466
11783
 
11467
11784
  // src/cli/commands/check.ts
11468
- import { existsSync as existsSync17 } from "node:fs";
11785
+ import { existsSync as existsSync18 } from "node:fs";
11469
11786
  import { check } from "@astrojs/check";
11470
11787
  import { sync } from "astro";
11471
11788
  import { defineCommand as defineCommand3 } from "citty";
11472
- import { join as join27 } from "pathe";
11789
+ import { join as join29 } from "pathe";
11473
11790
  var checkCommand = defineCommand3({
11474
11791
  args: {
11792
+ isolated: {
11793
+ description: "Type-check in an isolated .blume-verify runtime so a running dev server is untouched. For verifying changes while `blume dev` runs.",
11794
+ type: "boolean"
11795
+ },
11475
11796
  preview: {
11476
11797
  description: "Include drafts and unpublished CMS content.",
11477
11798
  type: "boolean"
@@ -11487,21 +11808,27 @@ var checkCommand = defineCommand3({
11487
11808
  },
11488
11809
  async run({ args }) {
11489
11810
  const root = process.cwd();
11811
+ const runtimeDir = args.isolated ? ".blume-verify" : process.env.BLUME_RUNTIME_DIR;
11812
+ refuseIfDevRunning(root, "checking", runtimeDir);
11813
+ if (args.isolated) {
11814
+ await ensureGitignore(root, [".blume-verify/"]);
11815
+ }
11490
11816
  const project = await prepareProject({
11491
11817
  mode: "build",
11492
11818
  preview: args.preview,
11493
11819
  root,
11820
+ runtimeDir,
11494
11821
  strict: args.strict
11495
11822
  });
11496
11823
  const { outDir } = project.context;
11497
11824
  await sync({ logLevel: "warn", root: outDir });
11498
- const tsconfig = join27(root, "tsconfig.json");
11825
+ const tsconfig = join29(root, "tsconfig.json");
11499
11826
  logger.start(`Type-checking ${project.graph.pages.length} page(s)`);
11500
11827
  const failed = await check({
11501
11828
  minimumFailingSeverity: "error",
11502
11829
  minimumSeverity: "hint",
11503
11830
  root: outDir,
11504
- tsconfig: existsSync17(tsconfig) ? tsconfig : undefined,
11831
+ tsconfig: existsSync18(tsconfig) ? tsconfig : undefined,
11505
11832
  watch: false
11506
11833
  });
11507
11834
  if (failed) {
@@ -11518,7 +11845,7 @@ import { dev } from "astro";
11518
11845
  import { defineCommand as defineCommand4 } from "citty";
11519
11846
 
11520
11847
  // src/astro/static-assets.ts
11521
- import { extname as extname7, join as join28, relative as relative15, resolve as resolve10, sep } from "pathe";
11848
+ import { extname as extname7, join as join30, relative as relative15, resolve as resolve10, sep } from "pathe";
11522
11849
 
11523
11850
  // src/astro/integration.ts
11524
11851
  var overlayServer = null;
@@ -11744,15 +12071,19 @@ var doctorCommand = defineCommand5({
11744
12071
  });
11745
12072
 
11746
12073
  // src/cli/commands/eject.ts
11747
- import { readFile as readFile15, writeFile as writeFile8 } from "node:fs/promises";
12074
+ import { readFile as readFile17, writeFile as writeFile9 } from "node:fs/promises";
11748
12075
  import { defineCommand as defineCommand6 } from "citty";
11749
- import { join as join30, relative as relative17 } from "pathe";
12076
+ import { join as join32, relative as relative17 } from "pathe";
11750
12077
 
11751
12078
  // 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";
12079
+ import { existsSync as existsSync19 } from "node:fs";
12080
+ import { cp, mkdir as mkdir5, readFile as readFile16, rm as rm2, writeFile as writeFile8 } from "node:fs/promises";
12081
+ import { join as join31, relative as relative16 } from "pathe";
11755
12082
  var POSIX = (path) => path.split("\\").join("/");
12083
+ var ejectOpenApiData = (project) => {
12084
+ const source = project.sources.find(isOpenApiSource);
12085
+ return source ? source.openApiData() : {};
12086
+ };
11756
12087
  var askFiles = async (project, srcDir, genDir) => {
11757
12088
  const { ask } = project.config.ai;
11758
12089
  if (!ask?.enabled) {
@@ -11762,14 +12093,14 @@ var askFiles = async (project, srcDir, genDir) => {
11762
12093
  const files = [
11763
12094
  {
11764
12095
  content: askEndpointTemplate(resolveAskBackend(ask), grounded),
11765
- path: join29(srcDir, "pages", "api", "ask.ts")
12096
+ path: join31(srcDir, "pages", "api", "ask.ts")
11766
12097
  }
11767
12098
  ];
11768
12099
  if (grounded) {
11769
12100
  files.push({
11770
12101
  content: `${JSON.stringify(await buildAskData(project))}
11771
12102
  `,
11772
- path: join29(genDir, "ask-data.json")
12103
+ path: join31(genDir, "ask-data.json")
11773
12104
  });
11774
12105
  }
11775
12106
  return files;
@@ -11777,15 +12108,15 @@ var askFiles = async (project, srcDir, genDir) => {
11777
12108
  var eject = async (root) => {
11778
12109
  const project = await scanProject(root, { mode: "build" });
11779
12110
  const { context, config } = project;
11780
- const srcDir = join29(root, "src");
11781
- const genDir = join29(srcDir, "generated");
12111
+ const srcDir = join31(root, "src");
12112
+ const genDir = join31(srcDir, "generated");
11782
12113
  const askEnabled = config.ai.ask?.enabled ?? false;
11783
12114
  const exportPdf = config.export.pdf;
11784
12115
  const exportEpub = config.export.epub;
11785
12116
  const [pages, needsReactRaw, userTheme, rawMarkdown, islands, examples] = await Promise.all([
11786
12117
  context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
11787
12118
  detectNeedsReact(root),
11788
- context.themeFile ? readFile14(context.themeFile, "utf-8") : Promise.resolve(""),
12119
+ context.themeFile ? readFile16(context.themeFile, "utf-8") : Promise.resolve(""),
11789
12120
  buildRawMarkdown(project),
11790
12121
  discoverIslands(root),
11791
12122
  discoverExamples(root, config.examples)
@@ -11822,18 +12153,19 @@ var eject = async (root) => {
11822
12153
  needsReact,
11823
12154
  needsSvelte,
11824
12155
  needsVue,
12156
+ openapiPath: "./src/generated/openapi.json",
11825
12157
  pages: relPages,
11826
12158
  searchClientPath: "./src/generated/search-client.ts",
11827
12159
  themePath: "./src/generated/app.css"
11828
12160
  }),
11829
- path: join29(root, "astro.config.mjs")
12161
+ path: join31(root, "astro.config.mjs")
11830
12162
  },
11831
12163
  {
11832
12164
  content: runtimeTsconfigTemplate(),
11833
- path: join29(root, "tsconfig.json"),
12165
+ path: join31(root, "tsconfig.json"),
11834
12166
  skipIfExists: true
11835
12167
  },
11836
- { content: envTemplate(), path: join29(srcDir, "env.d.ts") },
12168
+ { content: envTemplate(), path: join31(srcDir, "env.d.ts") },
11837
12169
  {
11838
12170
  content: contentConfigTemplate({
11839
12171
  config,
@@ -11841,7 +12173,7 @@ var eject = async (root) => {
11841
12173
  staged: hasStaged,
11842
12174
  stagedBase: stagedDir
11843
12175
  }),
11844
- path: join29(srcDir, "content.config.ts")
12176
+ path: join31(srcDir, "content.config.ts")
11845
12177
  },
11846
12178
  {
11847
12179
  content: catchAllPageTemplate({
@@ -11851,19 +12183,19 @@ var eject = async (root) => {
11851
12183
  mathEnabled: config.markdown.math,
11852
12184
  needsReact
11853
12185
  }),
11854
- path: join29(srcDir, "pages", "[...slug].astro")
12186
+ path: join31(srcDir, "pages", "[...slug].astro")
11855
12187
  },
11856
12188
  {
11857
12189
  content: planComponentSlots(componentsImport, null).module,
11858
- path: join29(genDir, "components.ts")
12190
+ path: join31(genDir, "components.ts")
11859
12191
  },
11860
12192
  {
11861
12193
  content: islandMapTemplate(islands.islands),
11862
- path: join29(genDir, "islands.ts")
12194
+ path: join31(genDir, "islands.ts")
11863
12195
  },
11864
12196
  {
11865
12197
  content: exampleMapTemplate(examples.examples),
11866
- path: join29(genDir, "examples.ts")
12198
+ path: join31(genDir, "examples.ts")
11867
12199
  },
11868
12200
  {
11869
12201
  content: tailwindEntryTemplate({
@@ -11875,21 +12207,26 @@ var eject = async (root) => {
11875
12207
  twoslashCss: twoslashCss(),
11876
12208
  userTheme
11877
12209
  }),
11878
- path: join29(genDir, "app.css")
12210
+ path: join31(genDir, "app.css")
12211
+ },
12212
+ { content: buildRuntimeData(project), path: join31(genDir, "data.json") },
12213
+ {
12214
+ content: `${JSON.stringify(ejectOpenApiData(project))}
12215
+ `,
12216
+ path: join31(genDir, "openapi.json")
11879
12217
  },
11880
- { content: buildRuntimeData(project), path: join29(genDir, "data.json") },
11881
12218
  {
11882
12219
  content: `${JSON.stringify(rawMarkdown)}
11883
12220
  `,
11884
- path: join29(genDir, "raw-markdown.json")
12221
+ path: join31(genDir, "raw-markdown.json")
11885
12222
  },
11886
12223
  {
11887
12224
  content: rawMarkdownEndpointTemplate(),
11888
- path: join29(srcDir, "pages", "[...slug].md.ts")
12225
+ path: join31(srcDir, "pages", "[...slug].md.ts")
11889
12226
  },
11890
12227
  {
11891
12228
  content: rawMarkdownEndpointTemplate(),
11892
- path: join29(srcDir, "pages", "[...slug].mdx.ts")
12229
+ path: join31(srcDir, "pages", "[...slug].mdx.ts")
11893
12230
  }
11894
12231
  ];
11895
12232
  if (askEnabled) {
@@ -11898,34 +12235,34 @@ var eject = async (root) => {
11898
12235
  if (config.seo.og.enabled) {
11899
12236
  files.push({
11900
12237
  content: ogEndpointTemplate(customOgRoutes(pages, config.title)),
11901
- path: join29(srcDir, "pages", "og", "[...slug].png.ts")
12238
+ path: join31(srcDir, "pages", "og", "[...slug].png.ts")
11902
12239
  });
11903
12240
  }
11904
12241
  if (!routeIsTaken(pages, project.graph.pages, "/404")) {
11905
12242
  files.push({
11906
12243
  content: notFoundPageTemplate(),
11907
- path: join29(srcDir, "pages", "404.astro")
12244
+ path: join31(srcDir, "pages", "404.astro")
11908
12245
  });
11909
12246
  }
11910
12247
  files.push({
11911
12248
  content: searchClientTemplate(config),
11912
- path: join29(genDir, "search-client.ts")
12249
+ path: join31(genDir, "search-client.ts")
11913
12250
  });
11914
12251
  if (servesStaticIndex(config.search.provider)) {
11915
12252
  const documents = await buildSearchDocuments(project);
11916
12253
  files.push({
11917
12254
  content: `${JSON.stringify(documents)}
11918
12255
  `,
11919
- path: join29(genDir, "search.json")
12256
+ path: join31(genDir, "search.json")
11920
12257
  }, {
11921
12258
  content: searchEndpointTemplate(),
11922
- path: join29(srcDir, "pages", "blume-search.json.ts")
12259
+ path: join31(srcDir, "pages", "blume-search.json.ts")
11923
12260
  });
11924
12261
  }
11925
12262
  if (config.search.provider === "mixedbread") {
11926
12263
  files.push({
11927
12264
  content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
11928
- path: join29(srcDir, "pages", "api", "search.ts")
12265
+ path: join31(srcDir, "pages", "api", "search.ts")
11929
12266
  });
11930
12267
  }
11931
12268
  const feeds = buildRssFeeds(project);
@@ -11934,13 +12271,13 @@ var eject = async (root) => {
11934
12271
  files.push({
11935
12272
  content: `${JSON.stringify(feedXml)}
11936
12273
  `,
11937
- path: join29(genDir, "rss.json")
12274
+ path: join31(genDir, "rss.json")
11938
12275
  }, {
11939
12276
  content: rssEndpointTemplate(),
11940
- path: join29(srcDir, "pages", "[section]", "rss.xml.ts")
12277
+ path: join31(srcDir, "pages", "[section]", "rss.xml.ts")
11941
12278
  });
11942
12279
  }
11943
- if (hasReferences(config)) {
12280
+ if (hasScalarReferences(config)) {
11944
12281
  const references = await buildReferenceFiles({
11945
12282
  config,
11946
12283
  contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
@@ -11949,28 +12286,28 @@ var eject = async (root) => {
11949
12286
  for (const file of references.files) {
11950
12287
  files.push({
11951
12288
  content: file.content,
11952
- path: join29(srcDir, "pages", file.pagePath)
12289
+ path: join31(srcDir, "pages", file.pagePath)
11953
12290
  });
11954
12291
  }
11955
12292
  }
11956
12293
  files.push(...islands.islands.map((island) => ({
11957
12294
  content: islandWrapperTemplate(island),
11958
- path: join29(genDir, "islands", `${island.name}.astro`)
12295
+ path: join31(genDir, "islands", `${island.name}.astro`)
11959
12296
  })), ...examples.examples.map((example) => ({
11960
12297
  content: exampleWrapperTemplate(example),
11961
- path: join29(genDir, "examples", `${exampleSlug(example.path)}.astro`)
12298
+ path: join31(genDir, "examples", `${exampleSlug(example.path)}.astro`)
11962
12299
  })));
11963
12300
  for (const [entryId, content] of staged) {
11964
- files.push({ content, path: join29(root, stagedDir, entryId) });
12301
+ files.push({ content, path: join31(root, stagedDir, entryId) });
11965
12302
  }
11966
- const written = files.filter((file) => !(file.skipIfExists && existsSync18(file.path)));
12303
+ const written = files.filter((file) => !(file.skipIfExists && existsSync19(file.path)));
11967
12304
  await Promise.all(written.map(async (file) => {
11968
- await mkdir5(join29(file.path, ".."), { recursive: true });
11969
- await writeFile7(file.path, file.content, "utf-8");
12305
+ await mkdir5(join31(file.path, ".."), { recursive: true });
12306
+ await writeFile8(file.path, file.content, "utf-8");
11970
12307
  }));
11971
- const assetsSrc = join29(context.outDir, "public", "blume-assets");
11972
- if (existsSync18(assetsSrc)) {
11973
- await cp(assetsSrc, join29(root, "public", "blume-assets"), {
12308
+ const assetsSrc = join31(context.outDir, "public", "blume-assets");
12309
+ if (existsSync19(assetsSrc)) {
12310
+ await cp(assetsSrc, join31(root, "public", "blume-assets"), {
11974
12311
  recursive: true
11975
12312
  });
11976
12313
  }
@@ -11980,10 +12317,10 @@ var eject = async (root) => {
11980
12317
 
11981
12318
  // src/cli/commands/eject.ts
11982
12319
  var updatePackageScripts = async (root) => {
11983
- const pkgPath = join30(root, "package.json");
12320
+ const pkgPath = join32(root, "package.json");
11984
12321
  let pkg;
11985
12322
  try {
11986
- pkg = JSON.parse(await readFile15(pkgPath, "utf-8"));
12323
+ pkg = JSON.parse(await readFile17(pkgPath, "utf-8"));
11987
12324
  } catch {
11988
12325
  return;
11989
12326
  }
@@ -11994,7 +12331,7 @@ var updatePackageScripts = async (root) => {
11994
12331
  dev: "astro dev",
11995
12332
  preview: "astro preview"
11996
12333
  };
11997
- await writeFile8(pkgPath, `${JSON.stringify(pkg, null, 2)}
12334
+ await writeFile9(pkgPath, `${JSON.stringify(pkg, null, 2)}
11998
12335
  `, "utf-8");
11999
12336
  };
12000
12337
  var ejectCommand = defineCommand6({
@@ -12033,32 +12370,7 @@ The blume package remains importable.`);
12033
12370
  import { existsSync as existsSync20 } from "node:fs";
12034
12371
  import { mkdir as mkdir6, writeFile as writeFile10 } from "node:fs/promises";
12035
12372
  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
12373
+ import { basename as basename4, dirname as dirname13, isAbsolute as isAbsolute9, join as join33, relative as relative18 } from "pathe";
12062
12374
  var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
12063
12375
  var packageTemplate = (name, version) => `{
12064
12376
  "name": ${JSON.stringify(name)},
@@ -12106,7 +12418,7 @@ var STARTERS = {
12106
12418
  files: (dir) => [
12107
12419
  {
12108
12420
  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")
12421
+ path: join33(dir, "index.mdx")
12110
12422
  }
12111
12423
  ]
12112
12424
  },
@@ -12121,7 +12433,7 @@ var STARTERS = {
12121
12433
  files: (dir) => [
12122
12434
  {
12123
12435
  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")
12436
+ path: join33(dir, "index.mdx")
12125
12437
  },
12126
12438
  {
12127
12439
  content: `---
@@ -12132,7 +12444,7 @@ date: 2026-01-01
12132
12444
 
12133
12445
  The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
12134
12446
  `,
12135
- path: join32(dir, "changelog", "v1-0-0.mdx")
12447
+ path: join33(dir, "changelog", "v1-0-0.mdx")
12136
12448
  }
12137
12449
  ]
12138
12450
  },
@@ -12145,7 +12457,7 @@ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries besid
12145
12457
  Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
12146
12458
 
12147
12459
  Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
12148
- path: join32(dir, "index.mdx")
12460
+ path: join33(dir, "index.mdx")
12149
12461
  }
12150
12462
  ]
12151
12463
  },
@@ -12156,11 +12468,11 @@ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
12156
12468
  content: page("Introduction", "Get started with the SDK.", `# Introduction
12157
12469
 
12158
12470
  Install the SDK and make your first call. See [Installation](/installation).`),
12159
- path: join32(dir, "index.mdx")
12471
+ path: join33(dir, "index.mdx")
12160
12472
  },
12161
12473
  {
12162
12474
  content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
12163
- path: join32(dir, "installation.mdx")
12475
+ path: join33(dir, "installation.mdx")
12164
12476
  }
12165
12477
  ]
12166
12478
  }
@@ -12207,7 +12519,7 @@ var initCommand = defineCommand7({
12207
12519
  async run({ args }) {
12208
12520
  const root = process.cwd();
12209
12521
  const contentDir = args["content-dir"] ?? "docs";
12210
- if (isAbsolute8(contentDir) || relative18(root, join32(root, contentDir)).startsWith("..")) {
12522
+ if (isAbsolute9(contentDir) || relative18(root, join33(root, contentDir)).startsWith("..")) {
12211
12523
  logger.error(`Invalid --content-dir "${contentDir}" (must be a path inside the project).`);
12212
12524
  process.exit(1);
12213
12525
  }
@@ -12222,9 +12534,9 @@ var initCommand = defineCommand7({
12222
12534
  process.exit(1);
12223
12535
  }
12224
12536
  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)));
12537
+ const createdPackage = await writeFileSafe(join33(root, "package.json"), packageTemplate(toPackageName(basename4(root)), getBlumeVersion()));
12538
+ await writeFileSafe(join33(root, "blume.config.ts"), starter.config);
12539
+ await Promise.all(starter.files(contentDir).map((file) => writeFileSafe(join33(root, file.path), file.content)));
12228
12540
  const ignored = await ensureGitignore(root, [".blume/", "dist/"]);
12229
12541
  if (ignored.length > 0) {
12230
12542
  logger.success(`Added ${ignored.join(", ")} to .gitignore`);
@@ -12266,14 +12578,14 @@ import { defineCommand as defineCommand8 } from "citty";
12266
12578
 
12267
12579
  // src/migrate/fumadocs/index.ts
12268
12580
  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";
12581
+ import { mkdir as mkdir8, readFile as readFile19, rename as rename3, rm as rm3, writeFile as writeFile12 } from "node:fs/promises";
12582
+ import { dirname as dirname16, join as join36, relative as relative19 } from "pathe";
12271
12583
  import { glob as glob8 } from "tinyglobby";
12272
12584
 
12273
12585
  // src/migrate/fumadocs/config.ts
12274
12586
  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";
12587
+ import { readFile as readFile18 } from "node:fs/promises";
12588
+ import { basename as basename5, dirname as dirname14, join as join34 } from "pathe";
12277
12589
  var SOURCE_FILES = [
12278
12590
  "lib/source.ts",
12279
12591
  "app/source.ts",
@@ -12303,7 +12615,7 @@ var GENERIC_NAMES = new Set([
12303
12615
  var gitRepoRoot = (start) => {
12304
12616
  let dir = start;
12305
12617
  for (;; ) {
12306
- if (existsSync21(join33(dir, ".git"))) {
12618
+ if (existsSync21(join34(dir, ".git"))) {
12307
12619
  return dir;
12308
12620
  }
12309
12621
  const parent = dirname14(dir);
@@ -12315,12 +12627,12 @@ var gitRepoRoot = (start) => {
12315
12627
  };
12316
12628
  var bareName = (name) => name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
12317
12629
  var readTitle = async (root) => {
12318
- const packageJson = join33(root, "package.json");
12630
+ const packageJson = join34(root, "package.json");
12319
12631
  if (!existsSync21(packageJson)) {
12320
12632
  return "Documentation";
12321
12633
  }
12322
12634
  try {
12323
- const parsed = JSON.parse(await readFile17(packageJson, "utf-8"));
12635
+ const parsed = JSON.parse(await readFile18(packageJson, "utf-8"));
12324
12636
  const { name } = parsed;
12325
12637
  if (typeof name !== "string" || !name.trim()) {
12326
12638
  return "Documentation";
@@ -12341,11 +12653,11 @@ var readTitle = async (root) => {
12341
12653
  };
12342
12654
  var scrapeBaseUrl = async (root) => {
12343
12655
  for (const candidate of SOURCE_FILES) {
12344
- const file = join33(root, candidate);
12656
+ const file = join34(root, candidate);
12345
12657
  if (!existsSync21(file)) {
12346
12658
  continue;
12347
12659
  }
12348
- const base = BASE_URL.exec(await readFile17(file, "utf-8"))?.groups?.base;
12660
+ const base = BASE_URL.exec(await readFile18(file, "utf-8"))?.groups?.base;
12349
12661
  if (base) {
12350
12662
  return base;
12351
12663
  }
@@ -12620,7 +12932,7 @@ var normalizeFumadocsPageMeta = (value) => {
12620
12932
  // src/migrate/fumadocs/groups.ts
12621
12933
  import { existsSync as existsSync23, statSync as statSync2 } from "node:fs";
12622
12934
  import { mkdir as mkdir7, rename as rename2, writeFile as writeFile11 } from "node:fs/promises";
12623
- import { basename as basename6, join as join34 } from "pathe";
12935
+ import { basename as basename6, join as join35 } from "pathe";
12624
12936
 
12625
12937
  // src/migrate/fumadocs/meta.ts
12626
12938
  var SEPARATOR = /^---(?<label>.*)---$/u;
@@ -12765,16 +13077,16 @@ var isDirectory = (path) => {
12765
13077
  }
12766
13078
  };
12767
13079
  var resolveEntry = (docsDir, name) => {
12768
- if (!isInsideRoot2(docsDir, join34(docsDir, name))) {
13080
+ if (!isInsideRoot2(docsDir, join35(docsDir, name))) {
12769
13081
  return null;
12770
13082
  }
12771
13083
  for (const ext of PAGE_EXTS) {
12772
- const file = join34(docsDir, `${name}${ext}`);
13084
+ const file = join35(docsDir, `${name}${ext}`);
12773
13085
  if (existsSync23(file)) {
12774
13086
  return { kind: "file", path: file };
12775
13087
  }
12776
13088
  }
12777
- const folder = join34(docsDir, name);
13089
+ const folder = join35(docsDir, name);
12778
13090
  return isDirectory(folder) ? { kind: "folder", path: folder } : null;
12779
13091
  };
12780
13092
  var humanize2 = (name) => name.split(WORD_SPLIT3).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
@@ -12809,7 +13121,7 @@ var moveItemIntoGroup = async (item, docsDir, groupDir, label, warnings) => {
12809
13121
  warnings.push(`Sidebar entry "${item.name}" in section "${label}" matched no page or folder; skipped.`);
12810
13122
  return null;
12811
13123
  }
12812
- const dest = join34(groupDir, basename6(resolved.path));
13124
+ const dest = join35(groupDir, basename6(resolved.path));
12813
13125
  if (existsSync23(dest)) {
12814
13126
  warnings.push(`Skipped moving "${item.name}" into section "${label}" (target already exists).`);
12815
13127
  return null;
@@ -12837,7 +13149,7 @@ var reshapeSection = async (section, docsDir, order, warnings) => {
12837
13149
  }
12838
13150
  return;
12839
13151
  }
12840
- const groupDir = join34(docsDir, `(${section.label})`);
13152
+ const groupDir = join35(docsDir, `(${section.label})`);
12841
13153
  const sectionKeys = [];
12842
13154
  for (const item of section.items) {
12843
13155
  const key = await moveItemIntoGroup(item, docsDir, groupDir, section.label, warnings);
@@ -12850,7 +13162,7 @@ var reshapeSection = async (section, docsDir, order, warnings) => {
12850
13162
  }
12851
13163
  order.push(section.label);
12852
13164
  if (sectionKeys.length > 1) {
12853
- await writeFile11(join34(groupDir, "meta.ts"), renderMetaModule({ pages: sectionKeys }), "utf-8");
13165
+ await writeFile11(join35(groupDir, "meta.ts"), renderMetaModule({ pages: sectionKeys }), "utf-8");
12854
13166
  }
12855
13167
  };
12856
13168
  var reshapeFumadocsGroups = async (structure, docsDir) => {
@@ -12883,7 +13195,7 @@ var FUMADOCS_LEFTOVERS = [
12883
13195
  ];
12884
13196
  var movePage = async (abs, base, root) => {
12885
13197
  const rel = relative19(base, abs);
12886
- const dest = join35(root, "docs", rel);
13198
+ const dest = join36(root, "docs", rel);
12887
13199
  if (existsSync24(dest)) {
12888
13200
  return {
12889
13201
  includeWarnings: [],
@@ -12893,7 +13205,7 @@ var movePage = async (abs, base, root) => {
12893
13205
  unsupported: []
12894
13206
  };
12895
13207
  }
12896
- const raw = await readFile18(abs, "utf-8");
13208
+ const raw = await readFile19(abs, "utf-8");
12897
13209
  const included = await inlineFumadocsIncludes(raw, {
12898
13210
  filePath: abs,
12899
13211
  root: base
@@ -12930,12 +13242,12 @@ var writeMeta = async (dest, meta, rel, warnings) => {
12930
13242
  };
12931
13243
  var convertMeta = async (abs, base, root) => {
12932
13244
  const rel = relative19(base, abs);
12933
- const raw = await readFile18(abs, "utf-8");
13245
+ const raw = await readFile19(abs, "utf-8");
12934
13246
  let parsed;
12935
13247
  try {
12936
13248
  parsed = JSON.parse(raw);
12937
13249
  } catch {
12938
- const dest2 = join35(root, "docs", rel);
13250
+ const dest2 = join36(root, "docs", rel);
12939
13251
  if (existsSync24(dest2)) {
12940
13252
  return [`Skipped ${rel} (target already exists)`];
12941
13253
  }
@@ -12946,8 +13258,8 @@ var convertMeta = async (abs, base, root) => {
12946
13258
  ];
12947
13259
  }
12948
13260
  const dir = dirname16(rel) === "." ? "" : dirname16(rel);
12949
- const docsDir = join35(root, "docs", dir);
12950
- const dest = join35(docsDir, "meta.ts");
13261
+ const docsDir = join36(root, "docs", dir);
13262
+ const dest = join36(docsDir, "meta.ts");
12951
13263
  const structure = parseFumadocsPages(parsed.pages);
12952
13264
  if (structure.hasSections && !existsSync24(dest)) {
12953
13265
  const self = translateFumadocsSelfMeta(parsed);
@@ -12996,7 +13308,7 @@ var summarizePages = (results) => {
12996
13308
  };
12997
13309
  };
12998
13310
  var cleanupSourceDirs = async (root) => {
12999
- const docs = join35(root, "content", "docs");
13311
+ const docs = join36(root, "content", "docs");
13000
13312
  if (existsSync24(docs)) {
13001
13313
  const remaining = await glob8(["**/*"], { cwd: docs, dot: true });
13002
13314
  if (remaining.length > 0) {
@@ -13006,7 +13318,7 @@ var cleanupSourceDirs = async (root) => {
13006
13318
  }
13007
13319
  await rm3(docs, { force: true, recursive: true });
13008
13320
  }
13009
- const content = join35(root, "content");
13321
+ const content = join36(root, "content");
13010
13322
  if (existsSync24(content)) {
13011
13323
  const remaining = await glob8(["**/*"], { cwd: content, dot: true });
13012
13324
  if (remaining.length === 0) {
@@ -13017,7 +13329,7 @@ var cleanupSourceDirs = async (root) => {
13017
13329
  };
13018
13330
  var migrateFumadocsProject = async (root) => {
13019
13331
  const { config, warnings: configWarnings } = await loadFumadocsConfig(root);
13020
- const base = join35(root, SOURCE_DIR);
13332
+ const base = join36(root, SOURCE_DIR);
13021
13333
  if (!existsSync24(base)) {
13022
13334
  await writeBlumeConfig(root, config);
13023
13335
  return {
@@ -13077,9 +13389,41 @@ var migrateFumadocsProject = async (root) => {
13077
13389
 
13078
13390
  // src/migrate/mintlify/index.ts
13079
13391
  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";
13392
+ import { mkdir as mkdir9, readFile as readFile20, rename as rename4, rm as rm4, stat as stat2, writeFile as writeFile13 } from "node:fs/promises";
13393
+ import { dirname as dirname17, join as join37 } from "pathe";
13082
13394
  import { glob as glob9 } from "tinyglobby";
13395
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
13396
+ var hasFontFamily = (value) => {
13397
+ const object = asRecord(value);
13398
+ if (!object) {
13399
+ return false;
13400
+ }
13401
+ const named = (child) => typeof asRecord(child)?.family === "string";
13402
+ return typeof object.family === "string" || named(object.heading) || named(object.body);
13403
+ };
13404
+ var droppedChromeWarnings = (spec, config) => {
13405
+ const warnings = [];
13406
+ const navbar = asRecord(spec.navbar);
13407
+ if (navbar && (navbar.links || navbar.primary)) {
13408
+ 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.");
13409
+ }
13410
+ if (asRecord(spec.footer)?.socials) {
13411
+ warnings.push("Footer social links (footer.socials) have no blume.config equivalent and were dropped; add them with a Footer layout override.");
13412
+ }
13413
+ if (hasFontFamily(spec.fonts ?? spec.font) && !config.theme?.fonts) {
13414
+ 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.");
13415
+ }
13416
+ return warnings;
13417
+ };
13418
+ var droppedRedirectWarnings = (spec) => {
13419
+ const { dropped } = partitionMintlifyRedirects(spec);
13420
+ if (dropped.length === 0) {
13421
+ return [];
13422
+ }
13423
+ return [
13424
+ `Dropped ${dropped.length} dynamic redirect(s) Blume can't model as static path-to-path (${dropped.join(", ")}); re-add them as host-level rules (e.g. _redirects or vercel.json).`
13425
+ ];
13426
+ };
13083
13427
  var prune = (value) => {
13084
13428
  if (Array.isArray(value)) {
13085
13429
  return value.map(prune);
@@ -13108,13 +13452,13 @@ var writeBlumeConfig2 = async (root, config) => {
13108
13452
 
13109
13453
  export default defineConfig(${JSON.stringify(prune(config), null, 2)});
13110
13454
  `;
13111
- await writeFile13(join36(root, "blume.config.ts"), body, "utf-8");
13455
+ await writeFile13(join37(root, "blume.config.ts"), body, "utf-8");
13112
13456
  };
13113
13457
  var relocateAssets = async (root, segments) => {
13114
13458
  const served = [];
13115
13459
  const moved = [];
13116
13460
  for (const segment of segments) {
13117
- const source = join36(root, segment);
13461
+ const source = join37(root, segment);
13118
13462
  if (!existsSync25(source) || segment === "public") {
13119
13463
  continue;
13120
13464
  }
@@ -13123,11 +13467,11 @@ var relocateAssets = async (root, segments) => {
13123
13467
  served.push(segment);
13124
13468
  continue;
13125
13469
  }
13126
- const dest = join36(root, "public", segment);
13470
+ const dest = join37(root, "public", segment);
13127
13471
  if (existsSync25(dest)) {
13128
13472
  continue;
13129
13473
  }
13130
- await mkdir9(join36(root, "public"), { recursive: true });
13474
+ await mkdir9(join37(root, "public"), { recursive: true });
13131
13475
  await rename4(source, dest);
13132
13476
  moved.push(segment);
13133
13477
  }
@@ -13148,7 +13492,7 @@ var applyRelocatedAssets = (config, assets, warnings) => {
13148
13492
  }
13149
13493
  };
13150
13494
  var cleanupSnippets = async (root, kept, warnings) => {
13151
- const dir = join36(root, "snippets");
13495
+ const dir = join37(root, "snippets");
13152
13496
  if (!existsSync25(dir)) {
13153
13497
  return;
13154
13498
  }
@@ -13169,11 +13513,11 @@ var cleanupSnippets = async (root, kept, warnings) => {
13169
13513
  };
13170
13514
  var migrateMintlifyProject = async (root) => {
13171
13515
  const warnings = [];
13172
- const configFile = existsSync25(join36(root, "docs.json")) ? join36(root, "docs.json") : join36(root, "mint.json");
13516
+ const configFile = existsSync25(join37(root, "docs.json")) ? join37(root, "docs.json") : join37(root, "mint.json");
13173
13517
  let config;
13174
13518
  if (existsSync25(configFile)) {
13175
13519
  config = await loadMintlifyConfig(root, configFile);
13176
- const spec = JSON.parse(await readFile19(configFile, "utf-8"));
13520
+ const spec = JSON.parse(await readFile20(configFile, "utf-8"));
13177
13521
  const i18n = mintlifyI18n(spec);
13178
13522
  if (i18n) {
13179
13523
  config.i18n = i18n;
@@ -13182,6 +13526,12 @@ var migrateMintlifyProject = async (root) => {
13182
13526
  }
13183
13527
  warnings.push(`Mapped ${i18n.locales.length} languages to i18n.locales (default: ${i18n.defaultLocale}); review the locale labels.`);
13184
13528
  }
13529
+ const openapiSources = config.openapi?.sources ?? [];
13530
+ if (openapiSources.length > 0) {
13531
+ warnings.push(`Mapped ${openapiSources.length} OpenAPI spec source(s) to openapi.sources (native reference renderer); verify each spec path or URL resolves.`);
13532
+ }
13533
+ warnings.push(...droppedChromeWarnings(spec, config));
13534
+ warnings.push(...droppedRedirectWarnings(spec));
13185
13535
  } else {
13186
13536
  warnings.push("No docs.json or mint.json found; writing a default config.");
13187
13537
  config = { content: { root: "." }, title: "Documentation" };
@@ -13204,7 +13554,7 @@ var migrateMintlifyProject = async (root) => {
13204
13554
  const unsupported = new Set;
13205
13555
  const keptComponents = new Set;
13206
13556
  for (const file of files) {
13207
- const raw = await readFile19(file, "utf-8");
13557
+ const raw = await readFile20(file, "utf-8");
13208
13558
  const result = await transformMintlifyContent(raw, {
13209
13559
  filePath: file,
13210
13560
  root,
@@ -13239,7 +13589,7 @@ var migrateMintlifyProject = async (root) => {
13239
13589
  warnings.push(`Dropped unsupported page frontmatter keys: ${[...removedKeys].join(", ")}.`);
13240
13590
  }
13241
13591
  if (unsupported.size > 0) {
13242
- warnings.push(`Components without a Blume equivalent need manual review (use the OpenAPI reference instead): ${[...unsupported].join(", ")}.`);
13592
+ warnings.push(`Components without a Blume equivalent need manual review: ${[...unsupported].join(", ")}.`);
13243
13593
  }
13244
13594
  warnings.push("Review blume.config.ts; navigation, theme, and chrome were mapped from docs.json.");
13245
13595
  return { moved, warnings };
@@ -13247,8 +13597,8 @@ var migrateMintlifyProject = async (root) => {
13247
13597
 
13248
13598
  // src/migrate/nextra/index.ts
13249
13599
  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";
13600
+ import { mkdir as mkdir10, readFile as readFile21, rename as rename5, rm as rm5, writeFile as writeFile14 } from "node:fs/promises";
13601
+ import { basename as basename7, dirname as dirname18, extname as extname8, join as join38, relative as relative20 } from "pathe";
13252
13602
  import { glob as glob10 } from "tinyglobby";
13253
13603
 
13254
13604
  // src/migrate/nextra/content.ts
@@ -13530,7 +13880,7 @@ var planMetas = (metas, index) => {
13530
13880
  plan.metaByDir.set(dir, conversion.folderMeta);
13531
13881
  plan.consumedMetas.push(meta.abs);
13532
13882
  for (const [slug, title] of Object.entries(conversion.folderTitles)) {
13533
- plan.folderTitleByDir.set(normalizeDir(join37(dir, slug)), title);
13883
+ plan.folderTitleByDir.set(normalizeDir(join38(dir, slug)), title);
13534
13884
  }
13535
13885
  for (const [slug, label] of Object.entries(conversion.pageLabels)) {
13536
13886
  const pageAbs = index.pagesByDir.get(dir)?.get(slug);
@@ -13555,11 +13905,11 @@ var planMetas = (metas, index) => {
13555
13905
  };
13556
13906
  var movePage2 = async (abs, options) => {
13557
13907
  const rel = relative20(options.base, abs);
13558
- const dest = join37(options.root, "docs", rel);
13908
+ const dest = join38(options.root, "docs", rel);
13559
13909
  if (existsSync26(dest)) {
13560
13910
  return { moved: 0, removed: [], skipped: rel, unsupported: [] };
13561
13911
  }
13562
- const raw = await readFile20(abs, "utf-8");
13912
+ const raw = await readFile21(abs, "utf-8");
13563
13913
  const text = rewriteNextraCallouts(stripNextraImports(raw));
13564
13914
  const unsupported = unsupportedNextraComponents(text);
13565
13915
  const parsed = frontmatter_default(text);
@@ -13608,7 +13958,7 @@ var writeFolderMetas = async (root, plan) => {
13608
13958
  if (Object.keys(finalMeta).length === 0) {
13609
13959
  return;
13610
13960
  }
13611
- const dest = join37(root, "docs", dir, "meta.ts");
13961
+ const dest = join38(root, "docs", dir, "meta.ts");
13612
13962
  await mkdir10(dirname18(dest), { recursive: true });
13613
13963
  await writeFile14(dest, `import { defineMeta } from "blume";
13614
13964
 
@@ -13619,7 +13969,7 @@ export default defineMeta(${JSON.stringify(finalMeta, null, 2)});
13619
13969
  var relocateUnparseableMetas = async (root, metas) => {
13620
13970
  const warnings = [];
13621
13971
  await Promise.all(metas.map(async ({ abs, rel }) => {
13622
- const dest = join37(root, "docs", rel);
13972
+ const dest = join38(root, "docs", rel);
13623
13973
  if (existsSync26(dest)) {
13624
13974
  warnings.push(`Skipped ${rel} (target already exists)`);
13625
13975
  return;
@@ -13632,7 +13982,7 @@ var relocateUnparseableMetas = async (root, metas) => {
13632
13982
  };
13633
13983
  var buildConfig = (tabs) => tabs.length > 0 ? { navigation: { tabs }, title: "Documentation" } : { title: "Documentation" };
13634
13984
  var migrateNextraProject = async (root) => {
13635
- const sourceDir = SOURCE_DIRS.find((dir) => existsSync26(join37(root, dir)));
13985
+ const sourceDir = SOURCE_DIRS.find((dir) => existsSync26(join38(root, dir)));
13636
13986
  if (!sourceDir) {
13637
13987
  await writeBlumeConfig(root, { title: "Documentation" });
13638
13988
  return {
@@ -13642,7 +13992,7 @@ var migrateNextraProject = async (root) => {
13642
13992
  ]
13643
13993
  };
13644
13994
  }
13645
- const base = join37(root, sourceDir);
13995
+ const base = join38(root, sourceDir);
13646
13996
  const pageFiles = await glob10([PAGE_GLOB2], {
13647
13997
  absolute: true,
13648
13998
  cwd: base,
@@ -13657,7 +14007,7 @@ var migrateNextraProject = async (root) => {
13657
14007
  const metas = await Promise.all(metaFiles.map(async (abs) => ({
13658
14008
  abs,
13659
14009
  ext: extname8(abs),
13660
- raw: await readFile20(abs, "utf-8"),
14010
+ raw: await readFile21(abs, "utf-8"),
13661
14011
  rel: relative20(base, abs)
13662
14012
  })));
13663
14013
  const plan = planMetas(metas, index);
@@ -13684,14 +14034,14 @@ var migrateNextraProject = async (root) => {
13684
14034
 
13685
14035
  // src/migrate/starlight/index.ts
13686
14036
  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";
14037
+ import { readFile as readFile23, writeFile as writeFile15 } from "node:fs/promises";
14038
+ import { join as join40 } from "pathe";
13689
14039
  import { glob as glob11 } from "tinyglobby";
13690
14040
 
13691
14041
  // src/migrate/starlight/config.ts
13692
14042
  import { existsSync as existsSync27 } from "node:fs";
13693
- import { readFile as readFile21 } from "node:fs/promises";
13694
- import { join as join38 } from "pathe";
14043
+ import { readFile as readFile22 } from "node:fs/promises";
14044
+ import { join as join39 } from "pathe";
13695
14045
  var CONFIG_FILES = [
13696
14046
  "astro.config.mjs",
13697
14047
  "astro.config.mts",
@@ -13722,7 +14072,7 @@ var extractStarlightOptions = (source) => {
13722
14072
  return isLiteralObject(parsed) ? parsed : "unparseable";
13723
14073
  };
13724
14074
  var loadStarlightConfig = async (root) => {
13725
- const file = CONFIG_FILES.map((name) => join38(root, name)).find((path) => existsSync27(path));
14075
+ const file = CONFIG_FILES.map((name) => join39(root, name)).find((path) => existsSync27(path));
13726
14076
  if (!file) {
13727
14077
  return {
13728
14078
  options: {},
@@ -13731,7 +14081,7 @@ var loadStarlightConfig = async (root) => {
13731
14081
  ]
13732
14082
  };
13733
14083
  }
13734
- const source = await readFile21(file, "utf-8");
14084
+ const source = await readFile22(file, "utf-8");
13735
14085
  const result = extractStarlightOptions(source);
13736
14086
  if (result === "missing") {
13737
14087
  return {
@@ -14129,7 +14479,7 @@ var starlightI18n = (options) => {
14129
14479
  // src/migrate/starlight/index.ts
14130
14480
  var CONTENT_DIR = "src/content/docs";
14131
14481
  var transformPage = async (file) => {
14132
- const raw = await readFile22(file, "utf-8");
14482
+ const raw = await readFile23(file, "utf-8");
14133
14483
  let text = stripStarlightImports(raw);
14134
14484
  text = rewriteStarlightAsides(text);
14135
14485
  text = rewriteStarlightComponents(text);
@@ -14151,7 +14501,7 @@ var migrateStarlightProject = async (root) => {
14151
14501
  config.i18n = i18n;
14152
14502
  warnings.push(`Mapped ${i18n.locales.length} locale(s) to i18n (default: ${i18n.defaultLocale}); review the locale labels.`);
14153
14503
  }
14154
- const base = join39(root, CONTENT_DIR);
14504
+ const base = join40(root, CONTENT_DIR);
14155
14505
  if (!existsSync28(base)) {
14156
14506
  await writeBlumeConfig(root, config);
14157
14507
  return {
@@ -14243,7 +14593,7 @@ var migrateCommand = defineCommand8({
14243
14593
  import { existsSync as existsSync29 } from "node:fs";
14244
14594
  import { preview } from "astro";
14245
14595
  import { defineCommand as defineCommand9 } from "citty";
14246
- import { join as join40 } from "pathe";
14596
+ import { join as join41 } from "pathe";
14247
14597
  var previewCommand = defineCommand9({
14248
14598
  args: {
14249
14599
  host: { description: "Network host to bind.", type: "string" },
@@ -14257,7 +14607,7 @@ var previewCommand = defineCommand9({
14257
14607
  const root = process.cwd();
14258
14608
  const { config } = await loadConfig(root);
14259
14609
  const context = resolveProjectContext(root, config);
14260
- if (!existsSync29(join40(context.outDir, "astro.config.mjs"))) {
14610
+ if (!existsSync29(join41(context.outDir, "astro.config.mjs"))) {
14261
14611
  logger.error("No build found. Run `blume build` first.");
14262
14612
  process.exit(1);
14263
14613
  }
@@ -14275,7 +14625,7 @@ var previewCommand = defineCommand9({
14275
14625
  // src/cli/commands/sync.ts
14276
14626
  import { rm as rm6 } from "node:fs/promises";
14277
14627
  import { defineCommand as defineCommand10 } from "citty";
14278
- import { join as join41 } from "pathe";
14628
+ import { join as join42 } from "pathe";
14279
14629
  var syncCommand = defineCommand10({
14280
14630
  args: {
14281
14631
  force: {
@@ -14297,7 +14647,7 @@ var syncCommand = defineCommand10({
14297
14647
  if (args.force) {
14298
14648
  const { config } = await loadConfig(root);
14299
14649
  const context = resolveProjectContext(root, config);
14300
- await rm6(join41(context.outDir, "cache"), { force: true, recursive: true });
14650
+ await rm6(join42(context.outDir, "cache"), { force: true, recursive: true });
14301
14651
  logger.info("Cleared source cache.");
14302
14652
  }
14303
14653
  await prepareProject({
@@ -14314,11 +14664,11 @@ var syncCommand = defineCommand10({
14314
14664
  // src/cli/commands/validate.ts
14315
14665
  import { existsSync as existsSync31 } from "node:fs";
14316
14666
  import { defineCommand as defineCommand11 } from "citty";
14317
- import { join as join43 } from "pathe";
14667
+ import { join as join44 } from "pathe";
14318
14668
 
14319
14669
  // src/core/links.ts
14320
14670
  import { existsSync as existsSync30 } from "node:fs";
14321
- import { basename as basename8, join as join42 } from "pathe";
14671
+ import { basename as basename8, join as join43 } from "pathe";
14322
14672
  var HTTP = /^https?:\/\//iu;
14323
14673
  var PROTOCOL_RELATIVE = /^\/\//u;
14324
14674
  var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
@@ -14331,10 +14681,10 @@ var STATUS_GONE = 410;
14331
14681
  var STATUS_METHOD_NOT_ALLOWED = 405;
14332
14682
  var STATUS_NOT_IMPLEMENTED = 501;
14333
14683
  var assetIsPresent = (resolved, ctx) => {
14334
- if (ctx.publicDir && existsSync30(join42(ctx.publicDir, resolved))) {
14684
+ if (ctx.publicDir && existsSync30(join43(ctx.publicDir, resolved))) {
14335
14685
  return true;
14336
14686
  }
14337
- return ctx.assetMounts.some((mount) => (resolved === mount.url || resolved.startsWith(`${mount.url}/`)) && existsSync30(join42(mount.dir, resolved.slice(mount.url.length))));
14687
+ return ctx.assetMounts.some((mount) => (resolved === mount.url || resolved.startsWith(`${mount.url}/`)) && existsSync30(join43(mount.dir, resolved.slice(mount.url.length))));
14338
14688
  };
14339
14689
  var isIndexPage = (page2) => {
14340
14690
  const ref = page2.source?.ref ?? page2.sourcePath ?? "";
@@ -14579,7 +14929,7 @@ var validateCommand = defineCommand11({
14579
14929
  try {
14580
14930
  const project = await scanProject(root, { mode: "build" });
14581
14931
  diagnostics.push(...project.diagnostics);
14582
- const publicDir = join43(root, "public");
14932
+ const publicDir = join44(root, "public");
14583
14933
  diagnostics.push(...await validateLinks(project.graph, {
14584
14934
  assetMounts: resolveAssetMounts(root, project.config.content.assets),
14585
14935
  checkExternal: Boolean(args.external),
@@ -14644,5 +14994,5 @@ process.on("unhandledRejection", (error) => {
14644
14994
  });
14645
14995
  runMain(main);
14646
14996
 
14647
- //# debugId=114B8B7438537BD664756E2164756E21
14997
+ //# debugId=A4775073F9612C8B64756E2164756E21
14648
14998
  //# sourceMappingURL=index.js.map