blume 1.6.4 → 1.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/bin/blume.mjs +3 -2
  3. package/dist/cli/chunk-0ewz4trd.js +679 -0
  4. package/dist/cli/chunk-0ewz4trd.js.map +15 -0
  5. package/dist/cli/chunk-27gtm2ym.js +69 -0
  6. package/dist/cli/chunk-27gtm2ym.js.map +11 -0
  7. package/dist/cli/chunk-2aj8ddew.js +72 -0
  8. package/dist/cli/chunk-2aj8ddew.js.map +10 -0
  9. package/dist/cli/chunk-3k0kzs6d.js +69 -0
  10. package/dist/cli/chunk-3k0kzs6d.js.map +11 -0
  11. package/dist/cli/chunk-3r94j3tc.js +221 -0
  12. package/dist/cli/chunk-3r94j3tc.js.map +10 -0
  13. package/dist/cli/chunk-4trphnvy.js +102 -0
  14. package/dist/cli/chunk-4trphnvy.js.map +11 -0
  15. package/dist/cli/chunk-4xyggvgf.js +21 -0
  16. package/dist/cli/chunk-4xyggvgf.js.map +10 -0
  17. package/dist/cli/chunk-5hs6gb7n.js +32 -0
  18. package/dist/cli/chunk-5hs6gb7n.js.map +10 -0
  19. package/dist/cli/chunk-5yvt556e.js +185 -0
  20. package/dist/cli/chunk-5yvt556e.js.map +11 -0
  21. package/dist/cli/chunk-62qsssnh.js +3808 -0
  22. package/dist/cli/chunk-62qsssnh.js.map +36 -0
  23. package/dist/cli/chunk-6kzzpsx8.js +26 -0
  24. package/dist/cli/chunk-6kzzpsx8.js.map +10 -0
  25. package/dist/cli/chunk-8gnpdsn1.js +952 -0
  26. package/dist/cli/chunk-8gnpdsn1.js.map +12 -0
  27. package/dist/cli/chunk-9sh49q0h.js +30 -0
  28. package/dist/cli/chunk-9sh49q0h.js.map +10 -0
  29. package/dist/cli/chunk-aerwpe14.js +2370 -0
  30. package/dist/cli/chunk-aerwpe14.js.map +15 -0
  31. package/dist/cli/chunk-ag1zyr5x.js +176 -0
  32. package/dist/cli/chunk-ag1zyr5x.js.map +10 -0
  33. package/dist/cli/chunk-bawgnt8x.js +277 -0
  34. package/dist/cli/chunk-bawgnt8x.js.map +11 -0
  35. package/dist/cli/chunk-bcy492zc.js +16 -0
  36. package/dist/cli/chunk-bcy492zc.js.map +10 -0
  37. package/dist/cli/chunk-btfr9yvw.js +41 -0
  38. package/dist/cli/chunk-btfr9yvw.js.map +10 -0
  39. package/dist/cli/chunk-cbjnx4s8.js +73 -0
  40. package/dist/cli/chunk-cbjnx4s8.js.map +10 -0
  41. package/dist/cli/chunk-cnvm6k3e.js +96 -0
  42. package/dist/cli/chunk-cnvm6k3e.js.map +10 -0
  43. package/dist/cli/chunk-etsqspj6.js +5170 -0
  44. package/dist/cli/chunk-etsqspj6.js.map +47 -0
  45. package/dist/cli/chunk-ev67ycx0.js +15 -0
  46. package/dist/cli/chunk-ev67ycx0.js.map +10 -0
  47. package/dist/cli/chunk-ey89bjj1.js +209 -0
  48. package/dist/cli/chunk-ey89bjj1.js.map +11 -0
  49. package/dist/cli/chunk-f75cqye8.js +76 -0
  50. package/dist/cli/chunk-f75cqye8.js.map +10 -0
  51. package/dist/cli/chunk-j00ezcg5.js +259 -0
  52. package/dist/cli/chunk-j00ezcg5.js.map +11 -0
  53. package/dist/cli/chunk-jtb45atp.js +467 -0
  54. package/dist/cli/chunk-jtb45atp.js.map +14 -0
  55. package/dist/cli/chunk-m3p3wahd.js +117 -0
  56. package/dist/cli/chunk-m3p3wahd.js.map +10 -0
  57. package/dist/cli/chunk-n0y172hf.js +387 -0
  58. package/dist/cli/chunk-n0y172hf.js.map +12 -0
  59. package/dist/cli/chunk-n4qjabmt.js +1062 -0
  60. package/dist/cli/chunk-n4qjabmt.js.map +25 -0
  61. package/dist/cli/chunk-nyqzjdhj.js +111 -0
  62. package/dist/cli/chunk-nyqzjdhj.js.map +11 -0
  63. package/dist/cli/chunk-pxj10x8y.js +35 -0
  64. package/dist/cli/chunk-pxj10x8y.js.map +10 -0
  65. package/dist/cli/chunk-s4jn7f1q.js +54 -0
  66. package/dist/cli/chunk-s4jn7f1q.js.map +10 -0
  67. package/dist/cli/chunk-s4k1pnvf.js +81 -0
  68. package/dist/cli/chunk-s4k1pnvf.js.map +10 -0
  69. package/dist/cli/chunk-s5e5jt53.js +227 -0
  70. package/dist/cli/chunk-s5e5jt53.js.map +11 -0
  71. package/dist/cli/chunk-sbdqrjbb.js +81 -0
  72. package/dist/cli/chunk-sbdqrjbb.js.map +10 -0
  73. package/dist/cli/chunk-tc89yh2r.js +136 -0
  74. package/dist/cli/chunk-tc89yh2r.js.map +10 -0
  75. package/dist/cli/chunk-vt8fgygt.js +23 -0
  76. package/dist/cli/chunk-vt8fgygt.js.map +10 -0
  77. package/dist/cli/chunk-vv237fp3.js +1002 -0
  78. package/dist/cli/chunk-vv237fp3.js.map +13 -0
  79. package/dist/cli/chunk-vv3f8mb6.js +5314 -0
  80. package/dist/cli/chunk-vv3f8mb6.js.map +58 -0
  81. package/dist/cli/chunk-vxv4x1n8.js +17 -0
  82. package/dist/cli/chunk-vxv4x1n8.js.map +10 -0
  83. package/dist/cli/chunk-wb067mv3.js +758 -0
  84. package/dist/cli/chunk-wb067mv3.js.map +13 -0
  85. package/dist/cli/chunk-wd27zjcz.js +60 -0
  86. package/dist/cli/chunk-wd27zjcz.js.map +10 -0
  87. package/dist/cli/chunk-wkq5tbtq.js +1141 -0
  88. package/dist/cli/chunk-wkq5tbtq.js.map +19 -0
  89. package/dist/cli/chunk-x1vrdjyk.js +1967 -0
  90. package/dist/cli/chunk-x1vrdjyk.js.map +34 -0
  91. package/dist/cli/chunk-x66c5yjn.js +23 -0
  92. package/dist/cli/chunk-x66c5yjn.js.map +10 -0
  93. package/dist/cli/index.js +55 -27587
  94. package/dist/cli/index.js.map +5 -243
  95. package/dist/types/ai/ask-context.d.ts +26 -0
  96. package/dist/types/core/code-fences.d.ts +11 -0
  97. package/dist/types/core/config-input.d.ts +10 -0
  98. package/dist/types/core/package-root.d.ts +1 -1
  99. package/dist/types/core/schema.d.ts +74 -1
  100. package/docs/02-deployment.mdx +1 -1
  101. package/docs/configuration/analytics.mdx +21 -2
  102. package/docs/configuration/ask-ai.mdx +1 -1
  103. package/docs/configuration/customization.mdx +2 -9
  104. package/docs/content/syntax.mdx +1 -1
  105. package/docs/reference/cli.mdx +1 -1
  106. package/package.json +16 -14
  107. package/src/ai/api/handlers.ts +4 -7
  108. package/src/ai/api/paths.ts +8 -0
  109. package/src/ai/api/spec.ts +2 -1
  110. package/src/ai/ask-context.ts +378 -22
  111. package/src/astro/generate.ts +25 -29
  112. package/src/astro/include-hmr.ts +10 -13
  113. package/src/astro/include-refresh.ts +0 -0
  114. package/src/astro/index.ts +6 -1
  115. package/src/astro/integration.ts +269 -53
  116. package/src/astro/module-types.ts +74 -0
  117. package/src/astro/templates.ts +85 -97
  118. package/src/audit/image-size.ts +10 -8
  119. package/src/cli/command-meta.ts +77 -0
  120. package/src/cli/commands/add.ts +2 -4
  121. package/src/cli/commands/audit.ts +2 -4
  122. package/src/cli/commands/build.ts +42 -346
  123. package/src/cli/commands/check.ts +2 -4
  124. package/src/cli/commands/dev.ts +31 -42
  125. package/src/cli/commands/doctor.ts +2 -4
  126. package/src/cli/commands/eject.ts +3 -41
  127. package/src/cli/commands/eval.ts +2 -5
  128. package/src/cli/commands/init.ts +2 -4
  129. package/src/cli/commands/mcp-stdio.ts +2 -5
  130. package/src/cli/commands/preview.ts +3 -5
  131. package/src/cli/commands/sync.ts +2 -4
  132. package/src/cli/commands/translate.ts +2 -5
  133. package/src/cli/commands/validate.ts +2 -4
  134. package/src/cli/commands/version.ts +2 -4
  135. package/src/cli/eject-scripts.ts +0 -45
  136. package/src/cli/host-args.ts +16 -0
  137. package/src/cli/index.ts +84 -35
  138. package/src/cli/lazy-command.ts +47 -0
  139. package/src/components/content/GithubInfo.astro +4 -1
  140. package/src/components/content/mermaid-element.ts +8 -0
  141. package/src/components/layout/Analytics.astro +20 -1
  142. package/src/components/layout/PageLayout.astro +14 -3
  143. package/src/components/layout/ReferenceLayout.astro +15 -4
  144. package/src/components/layout/RootLayout.astro +15 -4
  145. package/src/components/layout/analytics-client.ts +2 -1
  146. package/src/components/layout/page-locale.ts +29 -0
  147. package/src/components/openapi/AsyncApiOperation.astro +5 -3
  148. package/src/components/openapi/Authorization.astro +4 -6
  149. package/src/components/openapi/Bindings.astro +2 -2
  150. package/src/components/openapi/Description.astro +109 -0
  151. package/src/components/openapi/GraphqlFieldsTable.astro +5 -7
  152. package/src/components/openapi/GraphqlOperation.astro +4 -3
  153. package/src/components/openapi/GraphqlType.astro +4 -6
  154. package/src/components/openapi/ParametersTable.astro +5 -7
  155. package/src/components/openapi/RequestBody.astro +2 -4
  156. package/src/components/openapi/Responses.astro +4 -3
  157. package/src/components/openapi/SchemaProperty.astro +11 -6
  158. package/src/components/openapi/description.ts +91 -0
  159. package/src/core/api-name.ts +18 -0
  160. package/src/core/code-fences.ts +48 -0
  161. package/src/core/config-input.ts +10 -0
  162. package/src/core/content-assets.ts +3 -7
  163. package/src/core/includes.ts +3 -7
  164. package/src/core/package-root.ts +1 -1
  165. package/src/core/schema.ts +27 -0
  166. package/src/core/sources/normalize.ts +2 -37
  167. package/src/core/sources/obsidian.ts +3 -2
  168. package/src/core/svg-dimensions.ts +97 -0
  169. package/src/core/version-cut.ts +2 -2
  170. package/src/deploy/artifacts.ts +370 -0
  171. package/src/deploy/cloudflare-negotiation.ts +97 -32
  172. package/src/deploy/function-bundle.ts +66 -20
  173. package/src/deploy/sitemap.ts +6 -0
  174. package/src/deploy/vercel-negotiation.ts +8 -30
  175. package/src/og/card.ts +6 -12
  176. package/src/openapi/render-mdx.ts +9 -5
  177. package/src/registry/eject.ts +0 -2
  178. package/src/theme/entry.ts +9 -2
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ serverFeatures
4
+ } from "./chunk-9sh49q0h.js";
5
+ import {
6
+ reportInternalError
7
+ } from "./chunk-btfr9yvw.js";
8
+ import"./chunk-wd27zjcz.js";
9
+ import {
10
+ scanProject
11
+ } from "./chunk-etsqspj6.js";
12
+ import"./chunk-vv3f8mb6.js";
13
+ import"./chunk-27gtm2ym.js";
14
+ import"./chunk-4xyggvgf.js";
15
+ import {
16
+ packageRoot
17
+ } from "./chunk-6kzzpsx8.js";
18
+ import {
19
+ BlumeError,
20
+ flushStdout,
21
+ logger,
22
+ reportDiagnostics,
23
+ reportDiagnosticsJson
24
+ } from "./chunk-ey89bjj1.js";
25
+ import {
26
+ commandMeta
27
+ } from "./chunk-2aj8ddew.js";
28
+
29
+ // src/cli/commands/doctor.ts
30
+ import { readFileSync } from "node:fs";
31
+ import { defineCommand } from "citty";
32
+ import { join } from "pathe";
33
+ import { satisfies } from "semver";
34
+ var FALLBACK_NODE_RANGE = ">=22.12.0";
35
+ var supportedNodeRange = () => {
36
+ try {
37
+ const pkg = JSON.parse(readFileSync(join(packageRoot(), "package.json"), "utf-8"));
38
+ return pkg.engines?.node || FALLBACK_NODE_RANGE;
39
+ } catch {
40
+ return FALLBACK_NODE_RANGE;
41
+ }
42
+ };
43
+ var doctorCommand = defineCommand({
44
+ args: {
45
+ json: {
46
+ description: "Emit diagnostics as JSON on stdout (for CI/editors).",
47
+ type: "boolean"
48
+ }
49
+ },
50
+ meta: commandMeta.doctor,
51
+ async run({ args }) {
52
+ const root = process.cwd();
53
+ const diagnostics = [];
54
+ const nodeRange = supportedNodeRange();
55
+ if (!satisfies(process.versions.node, nodeRange)) {
56
+ diagnostics.push({
57
+ code: "BLUME_NODE_VERSION",
58
+ message: `Node ${process.versions.node} is outside the supported range (${nodeRange}).`,
59
+ severity: "warning"
60
+ });
61
+ }
62
+ try {
63
+ const project = await scanProject(root, { mode: "build" });
64
+ diagnostics.push(...project.diagnostics);
65
+ const { config } = project;
66
+ const features = serverFeatures(config);
67
+ if (features.length > 0 && config.deployment.output === "static") {
68
+ diagnostics.push({
69
+ code: "BLUME_SERVER_FEATURE_REQUIRED",
70
+ message: `${features.join(", ")} require server output.`,
71
+ severity: "error",
72
+ suggestion: 'Set deployment.output to "server".'
73
+ });
74
+ }
75
+ if (config.deployment.output === "server" && !config.deployment.adapter) {
76
+ diagnostics.push({
77
+ code: "BLUME_ADAPTER_REQUIRED",
78
+ message: "Server output requires an adapter.",
79
+ severity: "error",
80
+ suggestion: 'Set deployment.adapter (e.g. "vercel").'
81
+ });
82
+ }
83
+ if (!args.json) {
84
+ logger.info(`Pages: ${project.graph.pages.length}`);
85
+ logger.info(`Output: ${config.deployment.output}`);
86
+ logger.info(`Search: ${config.search.provider}`);
87
+ }
88
+ } catch (error) {
89
+ if (error instanceof BlumeError) {
90
+ diagnostics.push(error.diagnostic);
91
+ } else {
92
+ reportInternalError(error);
93
+ process.exit(1);
94
+ }
95
+ }
96
+ if (args.json) {
97
+ if (reportDiagnosticsJson(diagnostics, root)) {
98
+ await flushStdout();
99
+ process.exit(1);
100
+ }
101
+ return;
102
+ }
103
+ const hadErrors = reportDiagnostics(diagnostics, root);
104
+ if (diagnostics.length === 0) {
105
+ logger.success("No problems found.");
106
+ }
107
+ if (hadErrors) {
108
+ process.exit(1);
109
+ }
110
+ }
111
+ });
112
+ export {
113
+ doctorCommand
114
+ };
115
+
116
+ //# debugId=544A15E145B664EE64756E2164756E21
117
+ //# sourceMappingURL=chunk-m3p3wahd.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/commands/doctor.ts"],
4
+ "sourcesContent": [
5
+ "import { readFileSync } from \"node:fs\";\n\nimport { defineCommand } from \"citty\";\nimport { join } from \"pathe\";\nimport { satisfies } from \"semver\";\n\nimport { BlumeError } from \"../../core/diagnostics.ts\";\nimport { packageRoot } from \"../../core/package-root.ts\";\nimport { scanProject } from \"../../core/project-graph.ts\";\nimport { serverFeatures } from \"../../core/server-features.ts\";\nimport type { Diagnostic } from \"../../core/types.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport { reportInternalError } from \"../internal-error.ts\";\nimport {\n flushStdout,\n logger,\n reportDiagnostics,\n reportDiagnosticsJson,\n} from \"../log.ts\";\n\nconst FALLBACK_NODE_RANGE = \">=22.12.0\";\n\n/** The supported Node range, read from the package's own `engines` field so\n * doctor can never drift from what the package actually declares. */\nconst supportedNodeRange = (): string => {\n try {\n // SAFETY: this parses blume's own package.json; the optional fields cover\n // an `engines` block going missing, and a bad read falls to the catch.\n const pkg = JSON.parse(\n readFileSync(join(packageRoot(), \"package.json\"), \"utf-8\")\n ) as { engines?: { node?: string } };\n return pkg.engines?.node || FALLBACK_NODE_RANGE;\n } catch {\n return FALLBACK_NODE_RANGE;\n }\n};\n\nexport const doctorCommand = defineCommand({\n args: {\n json: {\n description: \"Emit diagnostics as JSON on stdout (for CI/editors).\",\n type: \"boolean\",\n },\n },\n meta: commandMeta.doctor,\n async run({ args }) {\n const root = process.cwd();\n const diagnostics: Diagnostic[] = [];\n\n const nodeRange = supportedNodeRange();\n if (!satisfies(process.versions.node, nodeRange)) {\n diagnostics.push({\n code: \"BLUME_NODE_VERSION\",\n message: `Node ${process.versions.node} is outside the supported range (${nodeRange}).`,\n severity: \"warning\",\n });\n }\n\n try {\n const project = await scanProject(root, { mode: \"build\" });\n diagnostics.push(...project.diagnostics);\n\n const { config } = project;\n const features = serverFeatures(config);\n if (features.length > 0 && config.deployment.output === \"static\") {\n diagnostics.push({\n code: \"BLUME_SERVER_FEATURE_REQUIRED\",\n message: `${features.join(\", \")} require server output.`,\n severity: \"error\",\n suggestion: 'Set deployment.output to \"server\".',\n });\n }\n if (config.deployment.output === \"server\" && !config.deployment.adapter) {\n diagnostics.push({\n code: \"BLUME_ADAPTER_REQUIRED\",\n message: \"Server output requires an adapter.\",\n severity: \"error\",\n suggestion: 'Set deployment.adapter (e.g. \"vercel\").',\n });\n }\n\n if (!args.json) {\n logger.info(`Pages: ${project.graph.pages.length}`);\n logger.info(`Output: ${config.deployment.output}`);\n logger.info(`Search: ${config.search.provider}`);\n }\n } catch (error) {\n if (error instanceof BlumeError) {\n diagnostics.push(error.diagnostic);\n } else {\n reportInternalError(error);\n process.exit(1);\n }\n }\n\n if (args.json) {\n // Drain stdout before exiting non-zero: `process.exit` would otherwise\n // truncate the JSON payload mid-write when stdout is a pipe — exactly how\n // `--json` is consumed in CI/editors.\n if (reportDiagnosticsJson(diagnostics, root)) {\n await flushStdout();\n process.exit(1);\n }\n return;\n }\n\n const hadErrors = reportDiagnostics(diagnostics, root);\n if (diagnostics.length === 0) {\n logger.success(\"No problems found.\");\n }\n if (hadErrors) {\n process.exit(1);\n }\n },\n});\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAEA;AACA;AACA;AAgBA,IAAM,sBAAsB;AAI5B,IAAM,qBAAqB,MAAc;AAAA,EACvC,IAAI;AAAA,IAGF,MAAM,MAAM,KAAK,MACf,aAAa,KAAK,YAAY,GAAG,cAAc,GAAG,OAAO,CAC3D;AAAA,IACA,OAAO,IAAI,SAAS,QAAQ;AAAA,IAC5B,MAAM;AAAA,IACN,OAAO;AAAA;AAAA;AAIJ,IAAM,gBAAgB,cAAc;AAAA,EACzC,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EACA,MAAM,YAAY;AAAA,OACZ,IAAG,GAAG,QAAQ;AAAA,IAClB,MAAM,OAAO,QAAQ,IAAI;AAAA,IACzB,MAAM,cAA4B,CAAC;AAAA,IAEnC,MAAM,YAAY,mBAAmB;AAAA,IACrC,IAAI,CAAC,UAAU,QAAQ,SAAS,MAAM,SAAS,GAAG;AAAA,MAChD,YAAY,KAAK;AAAA,QACf,MAAM;AAAA,QACN,SAAS,QAAQ,QAAQ,SAAS,wCAAwC;AAAA,QAC1E,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,IAEA,IAAI;AAAA,MACF,MAAM,UAAU,MAAM,YAAY,MAAM,EAAE,MAAM,QAAQ,CAAC;AAAA,MACzD,YAAY,KAAK,GAAG,QAAQ,WAAW;AAAA,MAEvC,QAAQ,WAAW;AAAA,MACnB,MAAM,WAAW,eAAe,MAAM;AAAA,MACtC,IAAI,SAAS,SAAS,KAAK,OAAO,WAAW,WAAW,UAAU;AAAA,QAChE,YAAY,KAAK;AAAA,UACf,MAAM;AAAA,UACN,SAAS,GAAG,SAAS,KAAK,IAAI;AAAA,UAC9B,UAAU;AAAA,UACV,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAAA,MACA,IAAI,OAAO,WAAW,WAAW,YAAY,CAAC,OAAO,WAAW,SAAS;AAAA,QACvE,YAAY,KAAK;AAAA,UACf,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UAAU;AAAA,UACV,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAAA,MAEA,IAAI,CAAC,KAAK,MAAM;AAAA,QACd,OAAO,KAAK,UAAU,QAAQ,MAAM,MAAM,QAAQ;AAAA,QAClD,OAAO,KAAK,WAAW,OAAO,WAAW,QAAQ;AAAA,QACjD,OAAO,KAAK,WAAW,OAAO,OAAO,UAAU;AAAA,MACjD;AAAA,MACA,OAAO,OAAO;AAAA,MACd,IAAI,iBAAiB,YAAY;AAAA,QAC/B,YAAY,KAAK,MAAM,UAAU;AAAA,MACnC,EAAO;AAAA,QACL,oBAAoB,KAAK;AAAA,QACzB,QAAQ,KAAK,CAAC;AAAA;AAAA;AAAA,IAIlB,IAAI,KAAK,MAAM;AAAA,MAIb,IAAI,sBAAsB,aAAa,IAAI,GAAG;AAAA,QAC5C,MAAM,YAAY;AAAA,QAClB,QAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,kBAAkB,aAAa,IAAI;AAAA,IACrD,IAAI,YAAY,WAAW,GAAG;AAAA,MAC5B,OAAO,QAAQ,oBAAoB;AAAA,IACrC;AAAA,IACA,IAAI,WAAW;AAAA,MACb,QAAQ,KAAK,CAAC;AAAA,IAChB;AAAA;AAEJ,CAAC;",
8
+ "debugId": "544A15E145B664EE64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,387 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ gradeExternal,
4
+ probeAll
5
+ } from "./chunk-cbjnx4s8.js";
6
+ import {
7
+ reportInternalError
8
+ } from "./chunk-btfr9yvw.js";
9
+ import {
10
+ customStaticRoutes,
11
+ discoverPages,
12
+ hasGeneratedChangelog
13
+ } from "./chunk-sbdqrjbb.js";
14
+ import {
15
+ isRelativeImageTarget,
16
+ resolveRelativeImage
17
+ } from "./chunk-5yvt556e.js";
18
+ import {
19
+ buildManifest,
20
+ localePrefix,
21
+ localizeRoute,
22
+ scanProject
23
+ } from "./chunk-etsqspj6.js";
24
+ import"./chunk-vv3f8mb6.js";
25
+ import {
26
+ normalizePath,
27
+ stripBasePath,
28
+ withBasePath
29
+ } from "./chunk-27gtm2ym.js";
30
+ import"./chunk-4xyggvgf.js";
31
+ import"./chunk-6kzzpsx8.js";
32
+ import {
33
+ BlumeError,
34
+ flushStdout,
35
+ logger,
36
+ reportDiagnostics,
37
+ reportDiagnosticsJson
38
+ } from "./chunk-ey89bjj1.js";
39
+ import {
40
+ commandMeta
41
+ } from "./chunk-2aj8ddew.js";
42
+
43
+ // src/cli/commands/validate.ts
44
+ import { existsSync as existsSync2 } from "node:fs";
45
+ import { defineCommand } from "citty";
46
+ import { join as join2 } from "pathe";
47
+
48
+ // src/core/links.ts
49
+ import { existsSync } from "node:fs";
50
+ import { basename, dirname, join, relative, resolve } from "pathe";
51
+
52
+ // src/core/locale-links.ts
53
+ var decodePercent = (value) => {
54
+ try {
55
+ return decodeURIComponent(value);
56
+ } catch {
57
+ return value;
58
+ }
59
+ };
60
+ var hasLocalePrefix = (path, i18n) => i18n.locales.some((locale) => {
61
+ const prefix = localePrefix(locale.code, i18n);
62
+ return prefix !== "" && (path === prefix || path.startsWith(`${prefix}/`));
63
+ });
64
+ var localizeLinkPath = (path, options) => {
65
+ const { basePath, i18n, locale, routes } = options;
66
+ if (localePrefix(locale, i18n) === "") {
67
+ return path;
68
+ }
69
+ const rest = normalizePath(stripBasePath(basePath, path));
70
+ if (hasLocalePrefix(rest, i18n)) {
71
+ return path;
72
+ }
73
+ const localized = withBasePath(basePath, localizeRoute(rest, locale, i18n));
74
+ return routes.has(localized) || routes.has(decodePercent(localized)) ? localized : path;
75
+ };
76
+ var routeSets = new WeakMap;
77
+
78
+ // src/core/links.ts
79
+ var HTTP = /^https?:\/\//iu;
80
+ var PROTOCOL_RELATIVE = /^\/\//u;
81
+ var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
82
+ var decodePercent2 = (value) => {
83
+ try {
84
+ return decodeURIComponent(value);
85
+ } catch {
86
+ return value;
87
+ }
88
+ };
89
+ var DOC_EXT = /\.(?:md|mdx)$/iu;
90
+ var FILE_EXT = /\.[a-z0-9]+$/iu;
91
+ var includedBySuffix = (link, page) => link.file ? ` (included by ${basename(page.sourcePath ?? page.id)})` : "";
92
+ var authoredImageTarget = (target, pagePath, partialPath) => {
93
+ const authored = relative(dirname(partialPath), resolve(dirname(pagePath), target));
94
+ return authored.startsWith(".") ? authored : `./${authored}`;
95
+ };
96
+ var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null && existsSync(join(ctx.publicDir, resolved));
97
+ var NUMERIC_PREFIX = /^\d+[-_.]/u;
98
+ var isIndexPage = (page) => /^index\.(?:md|mdx)$/iu.test(basename(page.navPath).replace(NUMERIC_PREFIX, ""));
99
+ var applyRelativePart = (segments, part) => {
100
+ if (part === "" || part === ".") {
101
+ return;
102
+ }
103
+ if (part === "..") {
104
+ segments.pop();
105
+ return;
106
+ }
107
+ segments.push(part);
108
+ };
109
+ var resolveRelative = (pageRoute, target, isIndex) => {
110
+ const segments = pageRoute.split("/").filter(Boolean);
111
+ if (!isIndex) {
112
+ segments.pop();
113
+ }
114
+ for (const part of target.split("/")) {
115
+ applyRelativePart(segments, part);
116
+ }
117
+ return `/${segments.join("/")}`;
118
+ };
119
+ var toRoute = (path) => {
120
+ let route = path.replace(DOC_EXT, "");
121
+ if (route.endsWith("/index")) {
122
+ route = route.slice(0, -"/index".length);
123
+ }
124
+ if (route.length > 1 && route.endsWith("/")) {
125
+ route = route.slice(0, -1);
126
+ }
127
+ return route === "" ? "/" : route;
128
+ };
129
+ var buildAnchorIndex = (pages) => {
130
+ const anchors = new Map;
131
+ for (const page of pages) {
132
+ anchors.set(page.route, new Set([
133
+ ...page.headings.map((heading) => heading.slug),
134
+ ...page.anchors
135
+ ]));
136
+ }
137
+ return anchors;
138
+ };
139
+ var checkAnchor = (route, fragment, site, ctx, via = "") => {
140
+ const anchors = ctx.anchors.get(route);
141
+ if (anchors?.has(fragment) || anchors?.has(fragment.toLowerCase())) {
142
+ return null;
143
+ }
144
+ return {
145
+ ...site,
146
+ code: "BLUME_BROKEN_ANCHOR",
147
+ message: `No anchor target on ${route} matches #${fragment}${via}.`,
148
+ severity: "warning"
149
+ };
150
+ };
151
+ var servedRoute = (authored, page, ctx) => ctx.i18n ? localizeLinkPath(authored, {
152
+ basePath: ctx.basePath,
153
+ i18n: ctx.i18n,
154
+ locale: page.locale,
155
+ routes: {
156
+ has: (route) => ctx.routes.has(route) || ctx.extraRoutes.has(route)
157
+ }
158
+ }) : authored;
159
+ var checkPathLink = (resolved, fragment, page, link, site, ctx, via = "") => {
160
+ const authoredRoute = toRoute(withBasePath(ctx.basePath, resolved));
161
+ const route = servedRoute(authoredRoute, page, ctx);
162
+ if (ctx.routes.has(route)) {
163
+ return fragment ? checkAnchor(route, fragment, site, ctx, via) : null;
164
+ }
165
+ if (ctx.extraRoutes.has(route)) {
166
+ return null;
167
+ }
168
+ if (ctx.redirects.has(route)) {
169
+ return null;
170
+ }
171
+ const assetPath = stripBasePath(ctx.basePath, resolved);
172
+ if (FILE_EXT.test(assetPath) && !DOC_EXT.test(assetPath)) {
173
+ if (assetIsPresent(assetPath, ctx)) {
174
+ return null;
175
+ }
176
+ if (link.image && page.sourcePath && isRelativeImageTarget(link.target)) {
177
+ if (resolveRelativeImage(dirname(page.sourcePath), link.target)) {
178
+ return null;
179
+ }
180
+ const authored = link.file ? authoredImageTarget(link.target, page.sourcePath, link.file) : link.target;
181
+ return {
182
+ ...site,
183
+ code: "BLUME_BROKEN_ASSET",
184
+ message: `Image ${authored} was not found next to ${basename(link.file ?? page.sourcePath)}.`,
185
+ severity: "warning",
186
+ suggestion: "Add the file next to the page source or fix the reference."
187
+ };
188
+ }
189
+ if (ctx.publicDir === null) {
190
+ return "asset-unchecked";
191
+ }
192
+ return {
193
+ ...site,
194
+ code: "BLUME_BROKEN_ASSET",
195
+ message: `Asset ${assetPath} was not found in the public directory.`,
196
+ severity: "warning",
197
+ suggestion: `Add the file at public${assetPath} or fix the link.`
198
+ };
199
+ }
200
+ return {
201
+ ...site,
202
+ code: "BLUME_BROKEN_LINK",
203
+ message: `Broken link to ${link.target}${via}: no page resolves to ${route}.`,
204
+ severity: "error",
205
+ suggestion: "Check the path, or create the target page."
206
+ };
207
+ };
208
+ var checkExternalLinks = async (refs) => {
209
+ const results = await probeAll(refs.map((ref) => ref.url));
210
+ const diagnostics = [];
211
+ for (const ref of refs) {
212
+ const result = results.get(ref.url);
213
+ const grade = result ? gradeExternal(result) : null;
214
+ if (grade) {
215
+ diagnostics.push({
216
+ code: "BLUME_DEAD_LINK",
217
+ column: ref.column,
218
+ file: ref.file,
219
+ line: ref.line,
220
+ message: `External link ${ref.url} is unreachable (${grade.detail}).`,
221
+ severity: grade.severity
222
+ });
223
+ }
224
+ }
225
+ return diagnostics;
226
+ };
227
+ var classifyLink = (page, link, ctx, onExternal) => {
228
+ const { target } = link;
229
+ const site = {
230
+ column: link.column,
231
+ file: link.file ?? page.sourcePath ?? page.id,
232
+ line: link.line
233
+ };
234
+ if (HTTP.test(target) || PROTOCOL_RELATIVE.test(target)) {
235
+ onExternal({
236
+ ...site,
237
+ url: PROTOCOL_RELATIVE.test(target) ? `https:${target}` : target
238
+ });
239
+ return null;
240
+ }
241
+ if (SCHEME.test(target)) {
242
+ return null;
243
+ }
244
+ const hashIndex = target.indexOf("#");
245
+ const fragment = decodePercent2(hashIndex === -1 ? "" : target.slice(hashIndex + 1));
246
+ let rawPath = hashIndex === -1 ? target : target.slice(0, hashIndex);
247
+ const queryIndex = rawPath.indexOf("?");
248
+ if (queryIndex !== -1) {
249
+ rawPath = rawPath.slice(0, queryIndex);
250
+ }
251
+ rawPath = decodePercent2(rawPath);
252
+ const via = rawPath.startsWith("/") ? "" : includedBySuffix(link, page);
253
+ if (rawPath === "") {
254
+ return fragment ? checkAnchor(page.route, fragment, site, ctx, via) : null;
255
+ }
256
+ const resolved = rawPath.startsWith("/") ? rawPath : resolveRelative(page.route, rawPath, isIndexPage(page));
257
+ return checkPathLink(resolved, fragment, page, link, site, ctx, via);
258
+ };
259
+ var validateLinks = async (graph, options) => {
260
+ const basePath = options.basePath ?? "";
261
+ const ctx = {
262
+ anchors: buildAnchorIndex(graph.pages),
263
+ basePath,
264
+ extraRoutes: new Set((options.extraRoutes ?? []).map(toRoute)),
265
+ i18n: options.i18n ?? null,
266
+ publicDir: options.publicDir,
267
+ redirects: new Set((options.redirects ?? []).map((redirect) => toRoute(withBasePath(basePath, redirect.from)))),
268
+ routes: new Set(graph.routes.keys())
269
+ };
270
+ const diagnostics = [];
271
+ const external = [];
272
+ let uncheckedAssets = 0;
273
+ for (const page of graph.pages) {
274
+ for (const link of page.links) {
275
+ const result = classifyLink(page, link, ctx, (ref) => external.push(ref));
276
+ if (result === "asset-unchecked") {
277
+ uncheckedAssets += 1;
278
+ } else if (result) {
279
+ diagnostics.push(result);
280
+ }
281
+ }
282
+ }
283
+ if (uncheckedAssets > 0) {
284
+ diagnostics.push({
285
+ code: "BLUME_ASSETS_UNCHECKED",
286
+ message: `${uncheckedAssets} asset link(s) not checked: no public/ directory found.`,
287
+ severity: "info"
288
+ });
289
+ }
290
+ if (options.checkExternal && external.length > 0) {
291
+ diagnostics.push(...await checkExternalLinks(external));
292
+ }
293
+ const seen = new Set;
294
+ return diagnostics.filter((diagnostic) => {
295
+ const key = [
296
+ diagnostic.code,
297
+ diagnostic.file ?? "",
298
+ diagnostic.line ?? "",
299
+ diagnostic.column ?? "",
300
+ diagnostic.message
301
+ ].join(`
302
+ `);
303
+ if (seen.has(key)) {
304
+ return false;
305
+ }
306
+ seen.add(key);
307
+ return true;
308
+ });
309
+ };
310
+
311
+ // src/cli/commands/validate.ts
312
+ var validateCommand = defineCommand({
313
+ args: {
314
+ external: {
315
+ description: "Check external (HTTP) links over the network.",
316
+ type: "boolean"
317
+ },
318
+ json: {
319
+ description: "Emit diagnostics as JSON on stdout (for CI/editors).",
320
+ type: "boolean"
321
+ },
322
+ strict: {
323
+ description: "Treat warnings as errors.",
324
+ type: "boolean"
325
+ }
326
+ },
327
+ meta: commandMeta.validate,
328
+ async run({ args }) {
329
+ const root = process.cwd();
330
+ const diagnostics = [];
331
+ try {
332
+ const project = await scanProject(root, { mode: "build" });
333
+ diagnostics.push(...project.diagnostics);
334
+ const userPages = project.context.pagesRoot ? await discoverPages(project.context.pagesRoot) : [];
335
+ const extraRoutes = customStaticRoutes(userPages);
336
+ if (hasGeneratedChangelog(project, userPages)) {
337
+ extraRoutes.push("/changelog");
338
+ }
339
+ if (project.config.i18n) {
340
+ const manifest = buildManifest({
341
+ config: project.config,
342
+ context: project.context,
343
+ graph: project.graph
344
+ });
345
+ extraRoutes.push(...manifest.routes.flatMap((route) => route.fallback ? [route.path] : []));
346
+ }
347
+ const publicDir = join2(root, "public");
348
+ diagnostics.push(...await validateLinks(project.graph, {
349
+ basePath: project.config.basePath,
350
+ checkExternal: Boolean(args.external),
351
+ extraRoutes,
352
+ i18n: project.config.i18n,
353
+ publicDir: existsSync2(publicDir) ? publicDir : null,
354
+ redirects: project.config.redirects
355
+ }));
356
+ } catch (error) {
357
+ if (error instanceof BlumeError) {
358
+ diagnostics.push(error.diagnostic);
359
+ } else {
360
+ reportInternalError(error);
361
+ process.exit(1);
362
+ }
363
+ }
364
+ const strictFailure = Boolean(args.strict) && diagnostics.some((diagnostic) => diagnostic.severity !== "info");
365
+ if (args.json) {
366
+ const hadErrors2 = reportDiagnosticsJson(diagnostics, root);
367
+ if (hadErrors2 || strictFailure) {
368
+ await flushStdout();
369
+ process.exit(1);
370
+ }
371
+ return;
372
+ }
373
+ const hadErrors = reportDiagnostics(diagnostics, root);
374
+ if (diagnostics.length === 0) {
375
+ logger.success("No broken links found.");
376
+ }
377
+ if (hadErrors || strictFailure) {
378
+ process.exit(1);
379
+ }
380
+ }
381
+ });
382
+ export {
383
+ validateCommand
384
+ };
385
+
386
+ //# debugId=2655D460EC2600A964756E2164756E21
387
+ //# sourceMappingURL=chunk-n0y172hf.js.map
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/commands/validate.ts", "../src/core/links.ts", "../src/core/locale-links.ts"],
4
+ "sourcesContent": [
5
+ "import { existsSync } from \"node:fs\";\n\nimport { defineCommand } from \"citty\";\nimport { join } from \"pathe\";\n\nimport {\n customStaticRoutes,\n discoverPages,\n hasGeneratedChangelog,\n} from \"../../astro/pages.ts\";\nimport { BlumeError } from \"../../core/diagnostics.ts\";\nimport { validateLinks } from \"../../core/links.ts\";\nimport { buildManifest } from \"../../core/manifest.ts\";\nimport { scanProject } from \"../../core/project-graph.ts\";\nimport type { Diagnostic } from \"../../core/types.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport { reportInternalError } from \"../internal-error.ts\";\nimport {\n flushStdout,\n logger,\n reportDiagnostics,\n reportDiagnosticsJson,\n} from \"../log.ts\";\n\nexport const validateCommand = defineCommand({\n args: {\n external: {\n description: \"Check external (HTTP) links over the network.\",\n type: \"boolean\",\n },\n json: {\n description: \"Emit diagnostics as JSON on stdout (for CI/editors).\",\n type: \"boolean\",\n },\n strict: {\n description: \"Treat warnings as errors.\",\n type: \"boolean\",\n },\n },\n meta: commandMeta.validate,\n async run({ args }) {\n const root = process.cwd();\n const diagnostics: Diagnostic[] = [];\n\n try {\n const project = await scanProject(root, { mode: \"build\" });\n // Surface content/graph problems too: a page that fails to parse is a\n // link-validation blind spot, so silently passing would be misleading.\n diagnostics.push(...project.diagnostics);\n\n // Custom `.astro` pages and the generated changelog index are servable\n // routes the content graph can't see — without them, a docs link to e.g.\n // a custom landing page fails as BLUME_BROKEN_LINK.\n const userPages = project.context.pagesRoot\n ? await discoverPages(project.context.pagesRoot)\n : [];\n const extraRoutes = customStaticRoutes(userPages);\n if (hasGeneratedChangelog(project, userPages)) {\n extraRoutes.push(\"/changelog\");\n }\n\n // Fallback-materialized locale routes (an untranslated page prerendered\n // at its localized URL) are servable but absent from the content graph —\n // without them a link to an untranslated sibling under a non-default\n // locale fails as BLUME_BROKEN_LINK even though the built site serves it.\n if (project.config.i18n) {\n const manifest = buildManifest({\n config: project.config,\n context: project.context,\n graph: project.graph,\n });\n extraRoutes.push(\n ...manifest.routes.flatMap((route) =>\n route.fallback ? [route.path] : []\n )\n );\n }\n\n const publicDir = join(root, \"public\");\n diagnostics.push(\n ...(await validateLinks(project.graph, {\n basePath: project.config.basePath,\n checkExternal: Boolean(args.external),\n extraRoutes,\n i18n: project.config.i18n,\n publicDir: existsSync(publicDir) ? publicDir : null,\n redirects: project.config.redirects,\n }))\n );\n } catch (error) {\n if (error instanceof BlumeError) {\n diagnostics.push(error.diagnostic);\n } else {\n reportInternalError(error);\n process.exit(1);\n }\n }\n\n // `--strict` escalates warnings to failures; info-level notes (e.g.\n // BLUME_ASSETS_UNCHECKED when there is no public/ dir) stay advisory.\n const strictFailure =\n Boolean(args.strict) &&\n diagnostics.some((diagnostic) => diagnostic.severity !== \"info\");\n\n if (args.json) {\n // Drain stdout before exiting non-zero: `process.exit` would otherwise\n // truncate the JSON payload mid-write when stdout is a pipe — exactly how\n // `--json` is consumed in CI/editors.\n const hadErrors = reportDiagnosticsJson(diagnostics, root);\n if (hadErrors || strictFailure) {\n await flushStdout();\n process.exit(1);\n }\n return;\n }\n\n const hadErrors = reportDiagnostics(diagnostics, root);\n if (diagnostics.length === 0) {\n logger.success(\"No broken links found.\");\n }\n if (hadErrors || strictFailure) {\n process.exit(1);\n }\n },\n});\n",
6
+ "import { existsSync } from \"node:fs\";\n\nimport { basename, dirname, join, relative, resolve } from \"pathe\";\n\nimport { stripBasePath, withBasePath } from \"./base-path.ts\";\nimport {\n isRelativeImageTarget,\n resolveRelativeImage,\n} from \"./content-assets.ts\";\nimport type { LocaleRouting } from \"./i18n.ts\";\nimport { localizeLinkPath } from \"./locale-links.ts\";\nimport { gradeExternal, probeAll } from \"./probe.ts\";\nimport type {\n ContentGraph,\n Diagnostic,\n PageLink,\n PageRecord,\n} from \"./types.ts\";\n\nconst HTTP = /^https?:\\/\\//iu;\nconst PROTOCOL_RELATIVE = /^\\/\\//u;\nconst SCHEME = /^[a-z][a-z0-9+.-]*:/iu;\n\n/** Percent-decode a link piece; malformed sequences stay verbatim. */\nconst decodePercent = (value: string): string => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n};\nconst DOC_EXT = /\\.(?:md|mdx)$/iu;\nconst FILE_EXT = /\\.[a-z0-9]+$/iu;\n\n/** Source position shared by every diagnostic raised for a link. */\ninterface LinkSite {\n column: number;\n file: string;\n line: number;\n}\n\n/**\n * Message context for a link written inside an included partial whose verdict\n * depends on *which* page splices it (relative paths resolve against the\n * including page's route; bare anchors against its headings). Without it the\n * same partial line can be reported broken while being valid from another\n * includer — with no way to tell the reports apart.\n */\nconst includedBySuffix = (link: PageLink, page: PageRecord): string =>\n link.file ? ` (included by ${basename(page.sourcePath ?? page.id)})` : \"\";\n\n/**\n * Invert the include image rebase for display: expansion rewrote a partial's\n * relative image target into the including page's directory so it resolves,\n * but the diagnostic points at the partial — report the path the author\n * actually wrote there, not the rebased one they never typed.\n */\nconst authoredImageTarget = (\n target: string,\n pagePath: string,\n partialPath: string\n): string => {\n const authored = relative(\n dirname(partialPath),\n resolve(dirname(pagePath), target)\n );\n return authored.startsWith(\".\") ? authored : `./${authored}`;\n};\n\n/** An external link occurrence queued for a network probe. */\ninterface ExternalRef extends LinkSite {\n url: string;\n}\n\n/** Lookups derived once from the content graph. */\ninterface LinkContext {\n anchors: Map<string, Set<string>>;\n /** Site-wide route mount point (`\"\"` or `/seg`); routes carry it, assets don't. */\n basePath: string;\n /** Servable routes outside the graph (custom pages, generated routes); their\n * headings are unknown, so anchors there are accepted unchecked. */\n extraRoutes: Set<string>;\n /** Locale routing, when the site is multi-locale; drives served-route resolution. */\n i18n: LocaleRouting | null;\n publicDir: string | null;\n /** Normalized `redirect.from` paths — valid targets that resolve at runtime. */\n redirects: Set<string>;\n routes: Set<string>;\n}\n\n/** Whether a resolved asset path exists under `public/`. */\nconst assetIsPresent = (resolved: string, ctx: LinkContext): boolean =>\n ctx.publicDir !== null && existsSync(join(ctx.publicDir, resolved));\n\n/** Outcome of classifying one link target. */\ntype LinkResult = Diagnostic | \"asset-unchecked\" | null;\n\n// Mirrors the ordering-prefix strip in `sources/normalize.ts`: route mapping\n// drops the prefix before recognizing `index`, so `01-index.mdx` is an index.\nconst NUMERIC_PREFIX = /^\\d+[-_.]/u;\n\n/**\n * Whether a page is a directory index (`…/index.md(x)`, ordering prefix\n * ignored). Its route already *is* its directory, so a relative link must\n * resolve against the route itself, not its parent — otherwise `./sibling`\n * from `guides/index.mdx` (route `/guides`) would resolve to `/sibling` and be\n * falsely flagged as broken. Tested against `navPath` — the locale-stripped\n * path — so a dot-parser localized index (`index.fr.mdx`) and a shared\n * locale-agnostic one (`index.$.mdx`) count too, matching how route mapping\n * recognizes them.\n */\nconst isIndexPage = (page: PageRecord): boolean =>\n /^index\\.(?:md|mdx)$/iu.test(\n basename(page.navPath).replace(NUMERIC_PREFIX, \"\")\n );\n\n/** Apply one relative-path segment to the accumulated route segments. */\nconst applyRelativePart = (segments: string[], part: string): void => {\n if (part === \"\" || part === \".\") {\n return;\n }\n if (part === \"..\") {\n segments.pop();\n return;\n }\n segments.push(part);\n};\n\n/** Resolve a relative link target against the directory of a page route. */\nconst resolveRelative = (\n pageRoute: string,\n target: string,\n isIndex: boolean\n): string => {\n const segments = pageRoute.split(\"/\").filter(Boolean);\n // Drop a leaf page's own segment so links resolve against its parent\n // directory. An index page's route already is its directory, so keep it.\n if (!isIndex) {\n segments.pop();\n }\n for (const part of target.split(\"/\")) {\n applyRelativePart(segments, part);\n }\n return `/${segments.join(\"/\")}`;\n};\n\n/** Normalize an internal path to its canonical route form. */\nconst toRoute = (path: string): string => {\n let route = path.replace(DOC_EXT, \"\");\n if (route.endsWith(\"/index\")) {\n route = route.slice(0, -\"/index\".length);\n }\n if (route.length > 1 && route.endsWith(\"/\")) {\n route = route.slice(0, -1);\n }\n return route === \"\" ? \"/\" : route;\n};\n\n/** Build a map of route -> set of heading anchor slugs. */\nconst buildAnchorIndex = (pages: PageRecord[]): Map<string, Set<string>> => {\n const anchors = new Map<string, Set<string>>();\n for (const page of pages) {\n anchors.set(\n page.route,\n new Set([\n ...page.headings.map((heading) => heading.slug),\n ...page.anchors,\n ])\n );\n }\n return anchors;\n};\n\n/** Verify a fragment resolves to a heading on the target route. */\nconst checkAnchor = (\n route: string,\n fragment: string,\n site: LinkSite,\n ctx: LinkContext,\n via = \"\"\n): Diagnostic | null => {\n const anchors = ctx.anchors.get(route);\n // Exact first: a `[#custom-id]` pin may contain uppercase, which the\n // lowercase fallback (lenient matching for slugger-generated ids) would miss.\n if (anchors?.has(fragment) || anchors?.has(fragment.toLowerCase())) {\n return null;\n }\n return {\n ...site,\n code: \"BLUME_BROKEN_ANCHOR\",\n message: `No anchor target on ${route} matches #${fragment}${via}.`,\n severity: \"warning\",\n };\n};\n\n/**\n * The route a link on `page` actually lands on. Rendered under a prefixed\n * locale, a root-relative link moves into that locale when the localized route\n * is served — a real translation (whose own headings then answer the anchor\n * check) or a fallback page (accepted unchecked via `extraRoutes`) — exactly as\n * `LocaleLinks.astro` rewrites it at render time. Otherwise the authored route\n * stands.\n */\nconst servedRoute = (\n authored: string,\n page: PageRecord,\n ctx: LinkContext\n): string =>\n ctx.i18n\n ? localizeLinkPath(authored, {\n basePath: ctx.basePath,\n i18n: ctx.i18n,\n locale: page.locale,\n routes: {\n has: (route) => ctx.routes.has(route) || ctx.extraRoutes.has(route),\n },\n })\n : authored;\n\n/** Validate a resolved internal path: asset, route, then optional anchor. */\nconst checkPathLink = (\n resolved: string,\n fragment: string,\n page: PageRecord,\n link: PageLink,\n site: LinkSite,\n ctx: LinkContext,\n via = \"\"\n): LinkResult => {\n // Page routes carry the site-wide base; an absolute author path is written\n // as if mounted at root, so base it for the route lookup (idempotent — a\n // relative link already resolved against the based `page.route`). A real\n // route always wins over the asset-extension heuristic, so a dotted route\n // (e.g. `/releases/v1.0`) isn't misread as a missing asset.\n const authoredRoute = toRoute(withBasePath(ctx.basePath, resolved));\n const route = servedRoute(authoredRoute, page, ctx);\n if (ctx.routes.has(route)) {\n return fragment ? checkAnchor(route, fragment, site, ctx, via) : null;\n }\n // A custom `.astro` page or generated route serves this path, but its\n // headings aren't indexed — accept any fragment rather than false-flag it.\n if (ctx.extraRoutes.has(route)) {\n return null;\n }\n // A configured `redirect.from` resolves at runtime, so it's a valid target.\n // Its destination (and any anchor there) is validated on its own page, so we\n // don't follow the redirect to check the fragment here.\n if (ctx.redirects.has(route)) {\n return null;\n }\n\n // Assets live in `public/` at the site root, unaffected by the base, so strip\n // it back off before probing the filesystem.\n const assetPath = stripBasePath(ctx.basePath, resolved);\n if (FILE_EXT.test(assetPath) && !DOC_EXT.test(assetPath)) {\n if (assetIsPresent(assetPath, ctx)) {\n return null;\n }\n // A colocated image embed (`![](./diagram.png)`) is resolved from beside\n // the page source and emitted to `_astro/` by the image pipeline, so it\n // never lands in `public/`. The *raw* target goes through the same\n // resolver that decides what the `/blume-assets/content` endpoint serves,\n // so a reference the rewriter skips (a `?v=2` suffix, a double-encoded\n // name) is reported here, not accepted. Plain links to the same path stay\n // on the public-dir probe — an href resolves as a site route, and only\n // image nodes are rewritten.\n if (link.image && page.sourcePath && isRelativeImageTarget(link.target)) {\n if (resolveRelativeImage(dirname(page.sourcePath), link.target)) {\n return null;\n }\n // A partial-origin image was rebased into the page's directory for\n // resolution but reports against the partial — cite the path as\n // authored there, so file, path, and \"next to\" agree.\n const authored = link.file\n ? authoredImageTarget(link.target, page.sourcePath, link.file)\n : link.target;\n return {\n ...site,\n code: \"BLUME_BROKEN_ASSET\",\n message: `Image ${authored} was not found next to ${basename(link.file ?? page.sourcePath)}.`,\n severity: \"warning\",\n suggestion:\n \"Add the file next to the page source or fix the reference.\",\n };\n }\n // Nowhere to look: no `public/` directory.\n if (ctx.publicDir === null) {\n return \"asset-unchecked\";\n }\n return {\n ...site,\n code: \"BLUME_BROKEN_ASSET\",\n message: `Asset ${assetPath} was not found in the public directory.`,\n severity: \"warning\",\n suggestion: `Add the file at public${assetPath} or fix the link.`,\n };\n }\n\n return {\n ...site,\n code: \"BLUME_BROKEN_LINK\",\n message: `Broken link to ${link.target}${via}: no page resolves to ${route}.`,\n severity: \"error\",\n suggestion: \"Check the path, or create the target page.\",\n };\n};\n\n/** Probe queued external links with bounded concurrency. */\nconst checkExternalLinks = async (\n refs: ExternalRef[]\n): Promise<Diagnostic[]> => {\n const results = await probeAll(refs.map((ref) => ref.url));\n\n const diagnostics: Diagnostic[] = [];\n for (const ref of refs) {\n const result = results.get(ref.url);\n const grade = result ? gradeExternal(result) : null;\n if (grade) {\n diagnostics.push({\n code: \"BLUME_DEAD_LINK\",\n column: ref.column,\n file: ref.file,\n line: ref.line,\n message: `External link ${ref.url} is unreachable (${grade.detail}).`,\n severity: grade.severity,\n });\n }\n }\n return diagnostics;\n};\n\n/** Classify a single link, queueing external refs via `onExternal`. */\nconst classifyLink = (\n page: PageRecord,\n link: PageLink,\n ctx: LinkContext,\n onExternal: (ref: ExternalRef) => void\n): LinkResult => {\n const { target } = link;\n const site: LinkSite = {\n column: link.column,\n // A link written inside an included partial reports against the partial\n // file, so the author fixes it where it lives.\n file: link.file ?? page.sourcePath ?? page.id,\n line: link.line,\n };\n\n if (HTTP.test(target) || PROTOCOL_RELATIVE.test(target)) {\n onExternal({\n ...site,\n url: PROTOCOL_RELATIVE.test(target) ? `https:${target}` : target,\n });\n return null;\n }\n if (SCHEME.test(target)) {\n // mailto:, tel:, and other non-HTTP schemes are not validated.\n return null;\n }\n\n const hashIndex = target.indexOf(\"#\");\n // Browser-copied links arrive percent-encoded (`/caf%C3%A9`, `#caf%C3%A9`)\n // while routes and anchor slugs are stored decoded — decode before comparing\n // so valid links aren't reported broken.\n const fragment = decodePercent(\n hashIndex === -1 ? \"\" : target.slice(hashIndex + 1)\n );\n let rawPath = hashIndex === -1 ? target : target.slice(0, hashIndex);\n const queryIndex = rawPath.indexOf(\"?\");\n if (queryIndex !== -1) {\n rawPath = rawPath.slice(0, queryIndex);\n }\n rawPath = decodePercent(rawPath);\n\n // A relative path (or a bare anchor) means something different from every\n // including page; an absolute path means the same thing everywhere — so\n // only includer-dependent targets carry the \"included by\" context, and\n // identical absolute-target reports collapse in the final dedupe.\n const via = rawPath.startsWith(\"/\") ? \"\" : includedBySuffix(link, page);\n if (rawPath === \"\") {\n return fragment ? checkAnchor(page.route, fragment, site, ctx, via) : null;\n }\n\n const resolved = rawPath.startsWith(\"/\")\n ? rawPath\n : resolveRelative(page.route, rawPath, isIndexPage(page));\n return checkPathLink(resolved, fragment, page, link, site, ctx, via);\n};\n\n/**\n * Validate every link discovered in the content graph: internal page links and\n * anchors against the route map, asset links against the public dir, and\n * (opt-in) external links over the network.\n */\nexport const validateLinks = async (\n graph: ContentGraph,\n options: {\n /** Site-wide route mount point (`\"\"` or `/seg`); routes and redirects carry it. */\n basePath?: string;\n /**\n * Servable routes the graph can't know: custom `.astro` pages and generated\n * routes (e.g. the `/changelog` index). Mounted outside `basePath` (they're\n * injected at their pattern), so they are *not* based here — mirroring the\n * full-route-set resolution in `nav-diagnostics.ts`/`generateRuntime`.\n */\n extraRoutes?: string[];\n /** Locale routing when i18n is on; links then resolve into the page's locale. */\n i18n?: LocaleRouting | null;\n publicDir: string | null;\n checkExternal?: boolean;\n /** Configured redirects; their `from` paths count as valid link targets. */\n redirects?: { from: string }[];\n }\n): Promise<Diagnostic[]> => {\n const basePath = options.basePath ?? \"\";\n const ctx: LinkContext = {\n anchors: buildAnchorIndex(graph.pages),\n basePath,\n extraRoutes: new Set((options.extraRoutes ?? []).map(toRoute)),\n i18n: options.i18n ?? null,\n publicDir: options.publicDir,\n redirects: new Set(\n (options.redirects ?? []).map((redirect) =>\n toRoute(withBasePath(basePath, redirect.from))\n )\n ),\n routes: new Set(graph.routes.keys()),\n };\n const diagnostics: Diagnostic[] = [];\n const external: ExternalRef[] = [];\n let uncheckedAssets = 0;\n\n for (const page of graph.pages) {\n for (const link of page.links) {\n const result = classifyLink(page, link, ctx, (ref) => external.push(ref));\n if (result === \"asset-unchecked\") {\n uncheckedAssets += 1;\n } else if (result) {\n diagnostics.push(result);\n }\n }\n }\n\n if (uncheckedAssets > 0) {\n diagnostics.push({\n code: \"BLUME_ASSETS_UNCHECKED\",\n message: `${uncheckedAssets} asset link(s) not checked: no public/ directory found.`,\n severity: \"info\",\n });\n }\n\n if (options.checkExternal && external.length > 0) {\n diagnostics.push(...(await checkExternalLinks(external)));\n }\n\n // A partial spliced into several pages (or every locale of one) yields the\n // same diagnostic once per including page record; byte-identical reports\n // add noise without information, so only the first of each survives.\n const seen = new Set<string>();\n return diagnostics.filter((diagnostic) => {\n const key = [\n diagnostic.code,\n diagnostic.file ?? \"\",\n diagnostic.line ?? \"\",\n diagnostic.column ?? \"\",\n diagnostic.message,\n ].join(\"\\n\");\n if (seen.has(key)) {\n return false;\n }\n seen.add(key);\n return true;\n });\n};\n",
7
+ "import {\n isInternalPath,\n normalizePath,\n stripBasePath,\n withBasePath,\n} from \"./base-path.ts\";\nimport { localePrefix, localizeRoute } from \"./i18n.ts\";\nimport type { LocaleRouting } from \"./i18n.ts\";\n\n/**\n * Locale-aware resolution of content links.\n *\n * Authors write internal links as if mounted at the default locale's root\n * (`[x](/guide)`, `<Card href=\"/guide\">`), and translated pages are usually\n * copies of the source with the same links — so read on `/fr/…`, a link would\n * otherwise drop the reader back into the default language. These helpers move\n * such a link to the reader's locale (`/fr/guide`) when that route is served\n * (a real translation or a materialized fallback page), and leave it alone\n * otherwise: an explicit cross-locale link (`/de/guide`), a custom `.astro`\n * page or generated route that has no per-locale variant, or a missing\n * translation on a site with fallbacks disabled all keep their authored\n * target rather than pointing at a 404.\n *\n * The rewrite runs at render time (`components/layout/LocaleLinks.astro`)\n * because content is compiled once per file but served per route: a fallback\n * route renders the fallback locale's file under the missing locale's URL, and\n * a shared `page.$.mdx` renders under every locale. The link checker applies\n * the same resolution so anchors are validated against the page a reader lands\n * on.\n */\n\n/** A route set, as the runtime (`Set`) or the checker (a predicate) sees it. */\nexport interface RouteSet {\n has: (route: string) => boolean;\n}\n\nexport interface LocalizeLinkOptions {\n /** Site-wide route mount point (`\"\"` or `/seg`); routes carry it. */\n basePath: string;\n i18n: LocaleRouting;\n /** Locale of the page the link is rendered on. */\n locale: string;\n /** Every served route, base-prefixed like `path` (no `deployment.base`). */\n routes: RouteSet;\n}\n\n/**\n * Mirrors the asset heuristic in `markdown/base-links.ts`: a path whose final\n * segment carries a file extension is a `public/` asset (or a raw `.md` twin),\n * served at the site root and never localized.\n */\nconst ASSET_PATH = /\\.[a-z0-9]+$/iu;\n\n/** Decode a percent-encoded path for a route lookup; leave junk as-is. */\nconst decodePercent = (value: string): string => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n};\n\n/** Whether `path` (base-stripped) already sits under some locale's prefix. */\nconst hasLocalePrefix = (path: string, i18n: LocaleRouting): boolean =>\n i18n.locales.some((locale) => {\n const prefix = localePrefix(locale.code, i18n);\n return prefix !== \"\" && (path === prefix || path.startsWith(`${prefix}/`));\n });\n\n/**\n * Move a base-prefixed, fragment-less internal path into `locale` when that\n * route is served, else return it unchanged. Idempotent: a path already under\n * any configured locale prefix is never re-prefixed.\n */\nexport const localizeLinkPath = (\n path: string,\n options: LocalizeLinkOptions\n): string => {\n const { basePath, i18n, locale, routes } = options;\n if (localePrefix(locale, i18n) === \"\") {\n return path;\n }\n const rest = normalizePath(stripBasePath(basePath, path));\n if (hasLocalePrefix(rest, i18n)) {\n return path;\n }\n const localized = withBasePath(basePath, localizeRoute(rest, locale, i18n));\n // Routes are stored decoded; a browser-copied `/caf%C3%A9` must still find\n // its translation, but the emitted href keeps the author's encoding.\n return routes.has(localized) || routes.has(decodePercent(localized))\n ? localized\n : path;\n};\n\nexport interface LocalizeHrefOptions extends LocalizeLinkOptions {\n /** `deployment.base` (Astro's `BASE_URL`), layered over `basePath` in hrefs. */\n deployBase: string;\n}\n\n/**\n * Localize a rendered `href`: external URLs, relative paths, bare fragments, and\n * asset links pass through; a root-relative page link keeps its `?query` and\n * `#fragment` and its `deployment.base` layer around the localized path.\n */\nexport const localizeHref = (\n href: string,\n options: LocalizeHrefOptions\n): string => {\n if (!isInternalPath(href)) {\n return href;\n }\n const suffixAt = href.search(/[#?]/u);\n const path = suffixAt === -1 ? href : href.slice(0, suffixAt);\n const suffix = suffixAt === -1 ? \"\" : href.slice(suffixAt);\n if (ASSET_PATH.test(path)) {\n return href;\n }\n const based = stripBasePath(options.deployBase, path);\n const localized = localizeLinkPath(based, options);\n if (localized === based) {\n return href;\n }\n return `${withBasePath(options.deployBase, localized)}${suffix}`;\n};\n\n/** Every `<a …>` opening tag; `\\s` keeps `<abbr>`/`<astro-island>` out. */\nconst ANCHOR_TAG = /<a\\s[^>]*>/giu;\n/** The tag's `href` attribute, double- or single-quoted. */\nconst HREF_ATTR = /(?<attr>\\shref=)(?:\"(?<dq>[^\"]*)\"|'(?<sq>[^']*)')/iu;\n\n/**\n * Rewrite every `<a href>` in rendered content HTML through `rewrite`. Code\n * blocks are HTML-escaped (`&lt;a`), and island props live on\n * `<astro-island>`, so neither is touched.\n */\nexport const localizeContentLinks = (\n html: string,\n rewrite: (href: string) => string\n): string =>\n html.replace(ANCHOR_TAG, (tag) =>\n tag.replace(HREF_ATTR, (_match, attr: string, dq?: string, sq?: string) => {\n const quote = dq === undefined ? \"'\" : '\"';\n return `${attr}${quote}${rewrite(dq ?? sq ?? \"\")}${quote}`;\n })\n );\n\n/**\n * The served route set for `blume:data`'s routes, built once per routes array\n * (the virtual module is evaluated once, so every page render shares it).\n */\nconst routeSets = new WeakMap<readonly { path: string }[], Set<string>>();\n\nexport const routeSetFor = (\n routes: readonly { path: string }[]\n): Set<string> => {\n const cached = routeSets.get(routes);\n if (cached) {\n return cached;\n }\n const built = new Set(routes.map((route) => route.path));\n routeSets.set(routes, built);\n return built;\n};\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "2655D460EC2600A964756E2164756E21",
11
+ "names": []
12
+ }