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,2370 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ API_CATALOG_PATH,
4
+ OPENAPI_PATH,
5
+ hasApiCatalog
6
+ } from "./chunk-3k0kzs6d.js";
7
+ import {
8
+ searchProviderMeta
9
+ } from "./chunk-wd27zjcz.js";
10
+ import {
11
+ BLUME_IGNORE_DIRS,
12
+ TOC_HIDDEN_KEY
13
+ } from "./chunk-etsqspj6.js";
14
+ import {
15
+ buildFontEntries,
16
+ fontLocaleCodes,
17
+ hasScalarReferences
18
+ } from "./chunk-vv3f8mb6.js";
19
+ import {
20
+ normalizeBasePath,
21
+ trimChar,
22
+ withBasePath,
23
+ withComposedBasePath
24
+ } from "./chunk-27gtm2ym.js";
25
+
26
+ // src/deploy/adapter-output.ts
27
+ import { existsSync } from "node:fs";
28
+ import { cp, mkdir, rm } from "node:fs/promises";
29
+ import { dirname, join } from "pathe";
30
+ var ADAPTER_IGNORE_DIRS = {
31
+ netlify: ".netlify/",
32
+ vercel: ".vercel/"
33
+ };
34
+ var ADAPTER_OUTPUT_PATHS = {
35
+ netlify: ".netlify/v1"
36
+ };
37
+ var servesClientSubdir = (deployment) => deployment.output === "server" && (deployment.adapter === "node" || deployment.adapter === "cloudflare");
38
+ var deployStaticDir = (config, context) => {
39
+ const { adapter, output } = config.deployment;
40
+ if (output === "server" && adapter === "vercel") {
41
+ return join(context.root, ".vercel", "output", "static");
42
+ }
43
+ const dist = context.distDir ?? join(context.root, "dist");
44
+ if (servesClientSubdir(config.deployment)) {
45
+ return join(dist, "client");
46
+ }
47
+ return dist;
48
+ };
49
+ var surfaceAdapterOutput = async (config, context) => {
50
+ const { adapter, output } = config.deployment;
51
+ if (output !== "server" || !adapter) {
52
+ return { moved: false };
53
+ }
54
+ const rel = ADAPTER_OUTPUT_PATHS[adapter];
55
+ if (!rel) {
56
+ return { moved: false };
57
+ }
58
+ const from = join(context.outDir, rel);
59
+ const to = join(context.root, rel);
60
+ if (!existsSync(from)) {
61
+ return { moved: false };
62
+ }
63
+ await mkdir(dirname(to), { recursive: true });
64
+ await rm(to, { force: true, recursive: true });
65
+ await cp(from, to, { recursive: true, verbatimSymlinks: true });
66
+ await rm(from, { force: true, recursive: true });
67
+ return { from, moved: true, to };
68
+ };
69
+
70
+ // src/ai/link-headers.ts
71
+ var buildHomeLinkHeader = (config, routePaths) => {
72
+ const deployBase = normalizeBasePath(config.deployment.base);
73
+ const links = [];
74
+ if (hasApiCatalog(config)) {
75
+ links.push(`<${deployBase}${API_CATALOG_PATH}>; rel="api-catalog"; type="application/linkset+json"`);
76
+ }
77
+ if (config.ai.api) {
78
+ links.push(`<${deployBase}${OPENAPI_PATH}>; rel="service-desc"; type="application/json"`);
79
+ }
80
+ if (config.seo.agentReadability) {
81
+ links.push(`<${deployBase}/agent-readability.json>; rel="describedby"; type="application/json"`);
82
+ }
83
+ if (config.ai.llmsTxt.enabled) {
84
+ links.push(`<${deployBase}/llms.txt>; rel="describedby"; type="text/plain"`);
85
+ }
86
+ if (routePaths.includes("/")) {
87
+ links.push(`<${deployBase}/index.md>; rel="alternate"; type="text/markdown"`);
88
+ }
89
+ return links.length > 0 ? links.join(", ") : null;
90
+ };
91
+
92
+ // src/deploy/redirects.ts
93
+ var applyBaseToAstroRedirects = (redirects, basePath, deployBase) => {
94
+ const base = normalizeBasePath(deployBase);
95
+ return basePath || base ? redirects.map((redirect) => ({
96
+ ...redirect,
97
+ from: withBasePath(basePath, redirect.from),
98
+ to: withComposedBasePath(base, basePath, redirect.to)
99
+ })) : redirects;
100
+ };
101
+ var applyBaseToPlatformRedirects = (redirects, basePath, deployBase) => {
102
+ const base = normalizeBasePath(deployBase);
103
+ return basePath || base ? redirects.map((redirect) => ({
104
+ ...redirect,
105
+ from: withComposedBasePath(base, basePath, redirect.from),
106
+ to: withComposedBasePath(base, basePath, redirect.to)
107
+ })) : redirects;
108
+ };
109
+ var platformRedirects = (config) => applyBaseToPlatformRedirects(config.redirects, config.basePath, config.deployment.base ?? "");
110
+
111
+ // src/ai/ask.ts
112
+ var OPENAI_COMPATIBLE = "openai-compatible";
113
+ var OPENAI_COMPATIBLE_DEP = "@ai-sdk/openai-compatible";
114
+ var ASK_PRESETS = {
115
+ inkeep: {
116
+ apiKeyEnv: "INKEEP_API_KEY",
117
+ baseUrl: "https://api.inkeep.com/v1",
118
+ kind: OPENAI_COMPATIBLE,
119
+ name: "inkeep",
120
+ runtimeDep: OPENAI_COMPATIBLE_DEP
121
+ },
122
+ llmgateway: {
123
+ apiKeyEnv: "LLMGATEWAY_API_KEY",
124
+ baseUrl: "https://api.llmgateway.io/v1",
125
+ kind: OPENAI_COMPATIBLE,
126
+ name: "llmgateway",
127
+ runtimeDep: OPENAI_COMPATIBLE_DEP
128
+ },
129
+ openrouter: {
130
+ apiKeyEnv: "OPENROUTER_API_KEY",
131
+ kind: "openrouter",
132
+ name: "openrouter",
133
+ runtimeDep: "@openrouter/ai-sdk-provider"
134
+ }
135
+ };
136
+ var DEFAULT_MODEL = "openai/gpt-5.5";
137
+ var resolveAskBackend = (ask) => {
138
+ const provider = ask?.provider ?? "gateway";
139
+ const model = ask?.model ?? DEFAULT_MODEL;
140
+ if (provider === "gateway") {
141
+ return { kind: "gateway", model };
142
+ }
143
+ const preset = ASK_PRESETS[provider];
144
+ const apiKeyEnv = ask?.apiKeyEnv ?? preset?.apiKeyEnv ?? "API_KEY";
145
+ if (provider === "openrouter") {
146
+ return { apiKeyEnv, kind: "openrouter", model };
147
+ }
148
+ return {
149
+ apiKeyEnv,
150
+ baseUrl: ask?.baseUrl ?? preset?.baseUrl ?? "",
151
+ kind: OPENAI_COMPATIBLE,
152
+ model,
153
+ name: preset?.name ?? OPENAI_COMPATIBLE
154
+ };
155
+ };
156
+ var askBackendRuntimeDep = (ask) => {
157
+ const provider = ask?.provider ?? "gateway";
158
+ if (provider === "gateway") {
159
+ return;
160
+ }
161
+ return ASK_PRESETS[provider]?.runtimeDep ?? OPENAI_COMPATIBLE_DEP;
162
+ };
163
+
164
+ // src/astro/runtime-modules.ts
165
+ var RUNTIME_MODULE_FILES = new Map([
166
+ ["blume:ask-data", "ask-data.json"],
167
+ ["blume:content-assets", "content-assets.json"],
168
+ ["blume:data", "data.json"],
169
+ ["blume:mcp-data", "mcp-data.json"],
170
+ ["blume:openapi", "openapi.json"],
171
+ ["blume:raw-markdown", "raw-markdown.json"],
172
+ ["blume:rss", "rss.json"],
173
+ ["blume:search-index", "search.json"]
174
+ ]);
175
+ var RUNTIME_MODULE_IDS = new Set(RUNTIME_MODULE_FILES.keys());
176
+ var RESOLVED_PREFIX = "\x00";
177
+ var REGISTRY_KEY = Symbol.for("blume.runtime-modules");
178
+ var registry = () => {
179
+ const host = globalThis;
180
+ host[REGISTRY_KEY] ??= { modules: new Map, servers: new Set };
181
+ return host[REGISTRY_KEY];
182
+ };
183
+ var publishRuntimeModules = (modules) => {
184
+ const { modules: current, servers } = registry();
185
+ const changed = [];
186
+ for (const id of RUNTIME_MODULE_FILES.keys()) {
187
+ const next = modules.get(id);
188
+ if (current.get(id) === next) {
189
+ continue;
190
+ }
191
+ if (next === undefined) {
192
+ current.delete(id);
193
+ } else {
194
+ current.set(id, next);
195
+ }
196
+ changed.push(id);
197
+ }
198
+ if (changed.length === 0) {
199
+ return changed;
200
+ }
201
+ for (const server of servers) {
202
+ for (const id of changed) {
203
+ const mod = server.moduleGraph.getModuleById(`${RESOLVED_PREFIX}${id}`);
204
+ if (mod) {
205
+ server.moduleGraph.invalidateModule(mod);
206
+ }
207
+ }
208
+ server.ws.send({ type: "full-reload" });
209
+ }
210
+ return changed;
211
+ };
212
+
213
+ // src/astro/templates.ts
214
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
215
+ import { pathToFileURL } from "node:url";
216
+ import { dirname as dirname2, isAbsolute, join as join2, relative } from "pathe";
217
+ var WORKSPACE_MARKERS = [
218
+ ".git",
219
+ "bun.lock",
220
+ "bun.lockb",
221
+ "package-lock.json",
222
+ "pnpm-lock.yaml",
223
+ "pnpm-workspace.yaml",
224
+ "yarn.lock"
225
+ ];
226
+ var hasWorkspacesField = (pkgPath) => {
227
+ if (!existsSync2(pkgPath)) {
228
+ return false;
229
+ }
230
+ try {
231
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
232
+ return pkg.workspaces !== undefined;
233
+ } catch {
234
+ return false;
235
+ }
236
+ };
237
+ var hasWorkspaceMarker = (dir) => hasWorkspacesField(join2(dir, "package.json")) || WORKSPACE_MARKERS.some((marker) => existsSync2(join2(dir, marker)));
238
+ var findWorkspaceRoot = (start) => {
239
+ let dir = start;
240
+ for (;; ) {
241
+ if (hasWorkspaceMarker(dir)) {
242
+ return dir;
243
+ }
244
+ const parent = dirname2(dir);
245
+ if (parent === dir) {
246
+ return start;
247
+ }
248
+ dir = parent;
249
+ }
250
+ };
251
+ var ADAPTER_IMPORTS = {
252
+ cloudflare: "@astrojs/cloudflare",
253
+ netlify: "@astrojs/netlify",
254
+ node: "@astrojs/node",
255
+ vercel: "@astrojs/vercel"
256
+ };
257
+ var ADAPTER_OPTIONS = new Map([
258
+ ["node", '{ mode: "standalone" }']
259
+ ]);
260
+ var WRANGLER_CONFIG_FILES = [
261
+ "wrangler.jsonc",
262
+ "wrangler.json",
263
+ "wrangler.toml"
264
+ ];
265
+ var resolveCloudflareAdapterArgs = (context) => {
266
+ const args = [
267
+ 'prerenderEnvironment: "node"',
268
+ 'imageService: "compile"'
269
+ ];
270
+ const wranglerPath = WRANGLER_CONFIG_FILES.map((file) => join2(context.root, file)).find((file) => existsSync2(file));
271
+ if (wranglerPath) {
272
+ let configPath = relative(context.outDir, wranglerPath);
273
+ if (!configPath.startsWith(".") && !configPath.startsWith("/")) {
274
+ configPath = `./${configPath}`;
275
+ }
276
+ args.push(`configPath: ${JSON.stringify(configPath)}`);
277
+ }
278
+ return `{ ${args.join(", ")} }`;
279
+ };
280
+ var resolveSessionOption = (deployment) => deployment.output === "server" && deployment.adapter === "cloudflare" ? `
281
+ session: false,` : "";
282
+ var astroConfigImportLine = (options) => {
283
+ const names = ["defineConfig"];
284
+ if (options.hasFonts) {
285
+ names.push("fontProviders");
286
+ }
287
+ return `import { ${names.join(", ")} } from "astro/config";`;
288
+ };
289
+ var runtimeDependencies = (options) => {
290
+ const { config, needsReact, needsSvelte, needsVue } = options;
291
+ const deps = ["@astrojs/mdx"];
292
+ if (needsReact) {
293
+ deps.push("@astrojs/react");
294
+ }
295
+ if (needsVue) {
296
+ deps.push("@astrojs/vue");
297
+ }
298
+ if (needsSvelte) {
299
+ deps.push("@astrojs/svelte");
300
+ }
301
+ if (hasScalarReferences(config)) {
302
+ deps.push("@scalar/astro");
303
+ }
304
+ deps.push(...searchProviderMeta(config.search.provider).runtimeDeps);
305
+ if (config.ai.ask?.enabled && !config.ai.ask.endpoint) {
306
+ const askDep = askBackendRuntimeDep(config.ai.ask);
307
+ if (askDep) {
308
+ deps.push(askDep);
309
+ }
310
+ }
311
+ const { deployment } = config;
312
+ if (deployment.output === "server" && deployment.adapter) {
313
+ const adapter = ADAPTER_IMPORTS[deployment.adapter];
314
+ if (adapter) {
315
+ deps.push(adapter);
316
+ }
317
+ }
318
+ return deps;
319
+ };
320
+ var RENDER_EXTERNAL_DEPS = [
321
+ "@astrojs/markdown-satteri",
322
+ "@pierre/diffs",
323
+ "@shikijs/transformers",
324
+ "@takumi-rs/core",
325
+ "@takumi-rs/helpers",
326
+ "@takumi-rs/wasm",
327
+ "github-slugger",
328
+ "katex",
329
+ "shiki",
330
+ "simple-icons",
331
+ "takumi-js",
332
+ "takumi-js/helpers",
333
+ "zod"
334
+ ];
335
+ var renderUserAliases = (aliases) => Object.entries(aliases ?? {}).toSorted(([a], [b]) => b.length - a.length).map(([find, replacement]) => `
336
+ ${JSON.stringify(find)}: ${JSON.stringify(replacement)},`).join("");
337
+ var astroOutDir = (context) => context.distDir ?? `${context.root}/dist`;
338
+ var adapterRoot = (context) => dirname2(astroOutDir(context));
339
+ var REACT_EXCLUDE = String.raw`exclude: [/\/node_modules\/\.vite\//, /\/\.cache\/vite\//]`;
340
+ var runtimeCacheOptions = (context, generatedModulesDir) => {
341
+ if (generatedModulesDir !== undefined) {
342
+ return { astro: "", vite: "" };
343
+ }
344
+ return {
345
+ astro: `
346
+ cacheDir: ${JSON.stringify(`${context.outDir}/.cache/astro`)},`,
347
+ vite: `
348
+ cacheDir: ${JSON.stringify(`${context.outDir}/.cache/vite`)},`
349
+ };
350
+ };
351
+ var reactIntegration = (compilerPath) => compilerPath ? `react({ babel: { plugins: [[${JSON.stringify(compilerPath)}, { target: "19" }]] }, ${REACT_EXCLUDE} })` : `react({ ${REACT_EXCLUDE} })`;
352
+ var renderIntegrationBridge = (bridge) => {
353
+ if (!bridge) {
354
+ return {
355
+ configSourceMarker: "",
356
+ userConfigImports: "",
357
+ userConfigSetup: "",
358
+ userIntegrationSpread: ""
359
+ };
360
+ }
361
+ return {
362
+ configSourceMarker: bridge.sourceHash ? `// Blume config source SHA-256: ${bridge.sourceHash}
363
+ ` : "",
364
+ userConfigImports: `import { dirname, resolve } from "node:path";
365
+ import { fileURLToPath } from "node:url";
366
+ import { createModuleLoader } from "blume/core/load-module.ts";
367
+ `,
368
+ userConfigSetup: `const loadBlumeConfig = createModuleLoader();
369
+ const blumeConfig = await loadBlumeConfig(resolve(dirname(fileURLToPath(import.meta.url)), ${JSON.stringify(bridge.configFile)}));
370
+
371
+ `,
372
+ userIntegrationSpread: ", ...(blumeConfig?.integrations ?? [])"
373
+ };
374
+ };
375
+ var renderImageOption = (config) => config.image.domains.length > 0 || config.image.remotePatterns.length > 0 ? `
376
+ image: ${JSON.stringify(config.image)},` : "";
377
+ var resolveOptimizeDeps = (options) => {
378
+ const { context } = options;
379
+ const optimizeDepsEntries = [
380
+ ...context.pagesRoot ? [`${context.pagesRoot}/**/*.astro`] : [],
381
+ `${context.root}/islands/**/*.{jsx,svelte,tsx,vue}`,
382
+ ...[...new Set(Object.values(options.aliases ?? {}))].toSorted().map((dir) => `${dir}/**/*.{astro,jsx,svelte,tsx,vue}`)
383
+ ];
384
+ const optimizeDepsInclude = [
385
+ "blume > mermaid",
386
+ "blume > epub-gen-memory/bundle",
387
+ ...options.needsReact && options.reactCompilerPath ? ["react/compiler-runtime"] : []
388
+ ];
389
+ return { optimizeDepsEntries, optimizeDepsInclude };
390
+ };
391
+ var renderRuntimeModuleWiring = (generatedModulesDir) => {
392
+ if (generatedModulesDir === undefined) {
393
+ return {
394
+ aliasLines: "",
395
+ imports: ["runtimeModulesPlugin"],
396
+ pluginEntry: "runtimeModulesPlugin(), "
397
+ };
398
+ }
399
+ const aliasLines = [...RUNTIME_MODULE_FILES].map(([id, file]) => `
400
+ ${JSON.stringify(id)}: ${JSON.stringify(`${generatedModulesDir}/${file}`)},`).join("");
401
+ return { aliasLines, imports: [], pluginEntry: "" };
402
+ };
403
+ var blumeIntegrationOptions = (options) => options.ejected ? {
404
+ buildArtifactsRoot: ".",
405
+ contentRoutes: options.contentRoutes,
406
+ homeLinkHeader: buildHomeLinkHeader(options.config, options.contentRoutes) ?? undefined,
407
+ pages: options.pages
408
+ } : { pages: options.pages };
409
+ var astroConfigTemplate = (options) => {
410
+ const { context, config, needsReact, pages, themePath } = options;
411
+ const { astro: cacheOptions, vite: viteCacheOption } = runtimeCacheOptions(context, options.generatedModulesDir);
412
+ const {
413
+ askPath,
414
+ contentRoutes,
415
+ examplesPath,
416
+ examplesThemePath,
417
+ generatedModulesDir,
418
+ needsSvelte,
419
+ needsVue,
420
+ searchClientPath
421
+ } = options;
422
+ const {
423
+ aliasLines: runtimeModuleAliasLines,
424
+ imports: runtimeModuleImports,
425
+ pluginEntry: runtimeModulesPluginEntry
426
+ } = renderRuntimeModuleWiring(generatedModulesDir);
427
+ const { deployment } = config;
428
+ const userAliasLines = renderUserAliases(options.aliases);
429
+ const server = deployment.output === "server";
430
+ const fsAllow = [...new Set([findWorkspaceRoot(context.root), context.root])];
431
+ const { optimizeDepsEntries, optimizeDepsInclude } = resolveOptimizeDeps({
432
+ aliases: options.aliases,
433
+ context,
434
+ needsReact,
435
+ reactCompilerPath: options.reactCompilerPath
436
+ });
437
+ const adapterImport = server && deployment.adapter ? `import adapter from "${ADAPTER_IMPORTS[deployment.adapter]}";
438
+ ` : "";
439
+ const adapterArgs = (() => {
440
+ if (!server || !deployment.adapter) {
441
+ return "";
442
+ }
443
+ if (deployment.adapter === "cloudflare") {
444
+ return resolveCloudflareAdapterArgs(context);
445
+ }
446
+ return ADAPTER_OPTIONS.get(deployment.adapter) ?? "";
447
+ })();
448
+ const adapterExpr = deployment.adapter === "vercel" ? `withAdapterRoot(adapter(${adapterArgs}), ${JSON.stringify(adapterRoot(context))})` : `adapter(${adapterArgs})`;
449
+ const adapterOption = server && deployment.adapter ? `
450
+ adapter: ${adapterExpr},` : "";
451
+ const sessionOption = resolveSessionOption(deployment);
452
+ const siteOption = deployment.site ? `
453
+ site: ${JSON.stringify(deployment.site)},` : "";
454
+ const baseOption = deployment.base ? `
455
+ base: ${JSON.stringify(deployment.base)},` : "";
456
+ const imageOption = renderImageOption(config);
457
+ const i18nOption = config.i18n ? `
458
+ i18n: ${JSON.stringify({
459
+ defaultLocale: config.i18n.defaultLocale,
460
+ locales: config.i18n.locales.map((locale) => locale.code),
461
+ routing: {
462
+ prefixDefaultLocale: !config.i18n.hideDefaultLocalePrefix
463
+ }
464
+ })},` : "";
465
+ const basedRedirects = applyBaseToAstroRedirects(config.redirects, config.basePath, deployment.base ?? "");
466
+ const redirectsOption = basedRedirects.length > 0 ? `
467
+ redirects: ${JSON.stringify(Object.fromEntries(basedRedirects.map((redirect) => [
468
+ redirect.from,
469
+ { destination: redirect.to, status: redirect.status }
470
+ ])))},` : "";
471
+ const fontEntries = buildFontEntries(config.theme.fonts, fontLocaleCodes(config.i18n));
472
+ const fontsOption = fontEntries.length ? `
473
+ fonts: [${fontEntries.map((font) => font.kind === "local" ? `{ provider: fontProviders.local(), name: ${JSON.stringify(font.name)}, cssVariable: ${JSON.stringify(font.cssVariable)}, fallbacks: ${JSON.stringify(font.fallbacks)}, options: { variants: ${JSON.stringify(font.variants.map((variant) => {
474
+ const face = {};
475
+ if (variant.weight !== undefined) {
476
+ face.weight = variant.weight;
477
+ }
478
+ if (variant.style !== undefined) {
479
+ face.style = variant.style;
480
+ }
481
+ return {
482
+ ...face,
483
+ src: [
484
+ isAbsolute(variant.src) ? variant.src : join2(context.root, variant.src)
485
+ ]
486
+ };
487
+ }))} } }` : `{ provider: fontProviders.${font.provider}(), name: ${JSON.stringify(font.name)}, cssVariable: ${JSON.stringify(font.cssVariable)}, weights: ${JSON.stringify(font.weights)}, subsets: ${JSON.stringify(font.subsets)}, fallbacks: ${JSON.stringify(font.fallbacks)} }`).join(", ")}],` : "";
488
+ const defineConfigImport = astroConfigImportLine({
489
+ hasFonts: fontEntries.length > 0
490
+ });
491
+ const reactImport = needsReact ? `import react from "@astrojs/react";
492
+ ` : "";
493
+ const vueImport = needsVue ? `import vue from "@astrojs/vue";
494
+ ` : "";
495
+ const svelteImport = needsSvelte ? `import svelte from "@astrojs/svelte";
496
+ ` : "";
497
+ const blumeImports = [
498
+ "blumeIntegration",
499
+ "includeHmrPlugin",
500
+ "prerenderDepsPlugin",
501
+ ...runtimeModuleImports,
502
+ ...adapterOption.includes("withAdapterRoot") ? ["withAdapterRoot"] : []
503
+ ];
504
+ const blumeImport = `import { ${blumeImports.join(", ")} } from "blume/astro";
505
+ `;
506
+ const twoslashTransformer = "blumeTwoslashTransformer(), ";
507
+ const deployBase = normalizeBasePath(deployment.base);
508
+ const integrations = [
509
+ `mdx({ processor: blumeMdxProcessor(${JSON.stringify({
510
+ basePath: config.basePath,
511
+ codeThemes: config.markdown.codeBlocks.theme,
512
+ contentRoot: options.contentRoot,
513
+ deployBase,
514
+ headingAnchors: config.markdown.headingAnchors
515
+ })}) })`
516
+ ];
517
+ if (needsReact) {
518
+ integrations.push(reactIntegration(options.reactCompilerPath));
519
+ }
520
+ if (needsVue) {
521
+ integrations.push("vue()");
522
+ }
523
+ if (needsSvelte) {
524
+ integrations.push("svelte()");
525
+ }
526
+ integrations.push(`blumeIntegration(${JSON.stringify(blumeIntegrationOptions({
527
+ config,
528
+ contentRoutes,
529
+ ejected: generatedModulesDir !== undefined,
530
+ pages
531
+ }))})`);
532
+ const {
533
+ configSourceMarker,
534
+ userConfigImports,
535
+ userConfigSetup,
536
+ userIntegrationSpread
537
+ } = renderIntegrationBridge(options.integrationBridge);
538
+ return `// Generated by Blume. Do not edit; this file is recreated on each run.
539
+ ${configSourceMarker}${userConfigImports}${defineConfigImport}
540
+ import mdx from "@astrojs/mdx";
541
+ import tailwindcss from "@tailwindcss/vite";
542
+ import { blumeMarkdownProcessor, blumeMdxProcessor, blumeShikiTransformers, blumeTwoslashTransformer } from "blume/markdown";
543
+ ${reactImport}${vueImport}${svelteImport}${blumeImport}${adapterImport}
544
+ ${userConfigSetup}export default defineConfig({
545
+ root: ${JSON.stringify(context.outDir)},
546
+ srcDir: ${JSON.stringify(`${context.outDir}/src`)},
547
+ outDir: ${JSON.stringify(astroOutDir(context))},
548
+ publicDir: ${JSON.stringify(`${context.root}/public`)},${cacheOptions}
549
+ output: ${JSON.stringify(deployment.output)},${adapterOption}${sessionOption}${siteOption}${baseOption}${imageOption}${redirectsOption}${i18nOption}${fontsOption}
550
+ integrations: [${integrations.join(", ")}${userIntegrationSpread}],
551
+ markdown: {
552
+ processor: blumeMarkdownProcessor(${JSON.stringify({
553
+ basePath: config.basePath,
554
+ codeThemes: config.markdown.codeBlocks.theme,
555
+ contentRoot: options.contentRoot,
556
+ deployBase,
557
+ headingAnchors: config.markdown.headingAnchors
558
+ })}),
559
+ shikiConfig: {
560
+ themes: {
561
+ light: ${JSON.stringify(config.markdown.codeBlocks.theme.light)},
562
+ dark: ${JSON.stringify(config.markdown.codeBlocks.theme.dark)},
563
+ },
564
+ defaultColor: false,
565
+ transformers: [${twoslashTransformer}...blumeShikiTransformers(${JSON.stringify({ icons: config.markdown.code.icons })})],
566
+ },
567
+ },
568
+ devToolbar: { enabled: false },
569
+ // One canonical URL per page: canonicals, the sitemap, and hreflang all use
570
+ // the slashless form, so the slashed spelling is not a second address. Astro
571
+ // applies this itself — its dev server answers a slashed URL with a 404 that
572
+ // names the setting, an on-demand route redirects — and the Vercel adapter
573
+ // turns it into the platform's 308 route, so the Build Output config needs
574
+ // no hand-spliced redirect (see deploy/vercel-negotiation.ts). Static hosts
575
+ // serve the \`index.html\` directory layout as they always did.
576
+ trailingSlash: "never",
577
+ // The layouts render Astro's <ClientRouter />, and its in-place swaps read
578
+ // from the prefetch cache — fetching every link on hover/viewport hides the
579
+ // request latency behind the user's intent, so most navigations swap
580
+ // instantly.
581
+ prefetch: { prefetchAll: true },
582
+ vite: {${viteCacheOption}
583
+ plugins: [${runtimeModulesPluginEntry}tailwindcss(), includeHmrPlugin(${JSON.stringify(`${context.outDir}/src/generated/includes.json`)}), prerenderDepsPlugin()],
584
+ // Everything hydration can reach must be part of the dev dep optimizer's
585
+ // FIRST run. The Vite root is the generated runtime, so user pages,
586
+ // islands, and aliased components live outside it and are only crawled
587
+ // when first requested — and \`react/compiler-runtime\` is Babel-injected,
588
+ // so no source scan can ever see it. A dependency discovered after
589
+ // hydration begins triggers a mid-session re-optimization whose new
590
+ // generation imports React through new \`?v=\` URLs; the browser then
591
+ // evaluates a second React copy and every island tears down with
592
+ // "Invalid hook call" (#157). \`entries\` points the startup scanner at
593
+ // the user's files (the scanner follows their imports, so their deps land
594
+ // in the initial optimization); the compiler runtime rides the include
595
+ // list because only the transform pipeline knows it exists.
596
+ //
597
+ // The mermaid/epub includes fix CJS interop instead: both lazy client-side
598
+ // imports land on CJS/UMD files (mermaid statically imports dayjs as CJS,
599
+ // epub-gen-memory's browser bundle is a browserified UMD) that break when
600
+ // served as raw ESM — mermaid throws on load and the EPUB export throws
601
+ // \`epub is not a function\`. They resolve through the \`blume\` package
602
+ // (they aren't direct deps of the generated project), so the nested
603
+ // \`blume > x\` form is required, and epub-gen-memory must name the
604
+ // \`/bundle\` subpath that is actually imported: optimizing the package
605
+ // root leaves that entry out. Production (Rollup) already handles the
606
+ // interop, so all of this only affects dev.
607
+ optimizeDeps: {
608
+ entries: ${JSON.stringify(optimizeDepsEntries)},
609
+ include: ${JSON.stringify(optimizeDepsInclude)},
610
+ },
611
+ // Blume's render-time deps are forced external on both build environments so
612
+ // native bindings resolve at runtime and isolated linkers don't bundle
613
+ // symlinked store copies (which would surface their children as unresolvable
614
+ // imports). See RENDER_EXTERNAL_DEPS / prerenderDepsPlugin.
615
+ //
616
+ // The SSR externals go through the legacy \`ssr.external\` key rather than
617
+ // \`environments.ssr\`: defining a user-owned \`environments.ssr\` block
618
+ // collides with the internal environment Astro 7 builds the server under and
619
+ // detaches the adapter's server entrypoint from the rolldown input, so the
620
+ // SSR entry is emitted as \`index.mjs\` instead of the \`entry.mjs\` the
621
+ // Vercel adapter's \`astro:build:done\` hook then fails to find. \`prerender\`
622
+ // is Astro-only and has no legacy equivalent, so it stays under \`environments\`.
623
+ ssr: { external: ${JSON.stringify(RENDER_EXTERNAL_DEPS)} },
624
+ environments: {
625
+ prerender: { resolve: { external: ${JSON.stringify(RENDER_EXTERNAL_DEPS)} } },
626
+ },
627
+ resolve: {
628
+ alias: {
629
+ "blume:ask": ${JSON.stringify(askPath)},
630
+ "blume:examples": ${JSON.stringify(examplesPath)},
631
+ "blume:examples-theme": ${JSON.stringify(examplesThemePath)},
632
+ "blume:search-client": ${JSON.stringify(searchClientPath)},
633
+ "blume:theme": ${JSON.stringify(themePath)},${runtimeModuleAliasLines}${userAliasLines}
634
+ },
635
+ },
636
+ server: {
637
+ fs: {
638
+ allow: ${JSON.stringify(fsAllow)},
639
+ },
640
+ },
641
+ },
642
+ });
643
+ `;
644
+ };
645
+ var stagedContentDir = (outDir) => join2(outDir, "content");
646
+ var runtimeDirWithin = (base, outDir) => {
647
+ const rel = relative(base, outDir);
648
+ return rel && !rel.startsWith("..") && !isAbsolute(rel) ? rel : null;
649
+ };
650
+ var astroGlobBase = (base) => isAbsolute(base) ? pathToFileURL(base).href : base;
651
+ var contentConfigTemplate = (options) => {
652
+ const { context, config } = options;
653
+ const stagedBase = options.stagedBase ?? stagedContentDir(context.outDir);
654
+ const collectionBase = options.collection?.base ?? context.contentRoot;
655
+ const includeGlobs = options.collection?.include ?? config.content.include;
656
+ const excludeGlobs = options.collection?.exclude ?? config.content.exclude;
657
+ const outDirRel = runtimeDirWithin(collectionBase, context.outDir);
658
+ const outDirIgnore = outDirRel ? [`!${outDirRel}/**`] : [];
659
+ const filesystem = options.filesystem ?? true;
660
+ const docsPattern = filesystem ? [
661
+ ...includeGlobs,
662
+ ...(excludeGlobs ?? []).map((pattern) => `!${pattern}`),
663
+ ...BLUME_IGNORE_DIRS.flatMap((dir) => dir === ".blume" ? [] : [`!**/${dir}/**`]),
664
+ ...outDirIgnore
665
+ ] : [];
666
+ const stagedBlock = options.staged ? `
667
+ const staged = defineCollection({
668
+ loader: glob({
669
+ pattern: ["**/*.{md,mdx}"],
670
+ base: ${JSON.stringify(astroGlobBase(stagedBase))},
671
+ generateId: ({ entry }) => entry,
672
+ }),
673
+ schema: pageCollectionSchema,
674
+ });
675
+ ` : "";
676
+ return `// Generated by Blume. Do not edit.
677
+ import { defineCollection } from "astro:content";
678
+ import { glob } from "astro/loaders";
679
+ import { withIncludeRefresh } from "blume/astro";
680
+ import { pageCollectionSchema } from "blume/core/schema.ts";
681
+
682
+ // withIncludeRefresh keeps <include>-bearing pages fresh: plain .md entries
683
+ // are rendered at sync time and digest-cached on the page file alone, so a
684
+ // partial edit (or a warm-cache rebuild after one) would serve stale HTML.
685
+ //
686
+ // pageCollectionSchema is the scan's page front-matter schema, so entry.data
687
+ // is typed and normalized the same way; it passes custom keys through and
688
+ // falls back to defaults for a page the scan already dropped as invalid.
689
+ const docs = defineCollection({
690
+ loader: withIncludeRefresh(glob({
691
+ pattern: ${JSON.stringify(docsPattern)},
692
+ base: ${JSON.stringify(astroGlobBase(collectionBase))},
693
+ generateId: ({ entry }) => entry,
694
+ }), ${JSON.stringify(`${context.outDir}/src/generated/includes.json`)}),
695
+ schema: pageCollectionSchema,
696
+ });
697
+ ${stagedBlock}
698
+ export const collections = { docs${options.staged ? ", staged" : ""} };
699
+ `;
700
+ };
701
+ var ASK_FALLBACK_PROMPT = "You are a helpful documentation assistant. Answer using the project's documentation.";
702
+ var askEndpointTemplate = (backend, grounded, options) => {
703
+ const instructions = options?.instructions;
704
+ const fallbackPrompt = instructions ? `${ASK_FALLBACK_PROMPT}
705
+
706
+ ${instructions}` : ASK_FALLBACK_PROMPT;
707
+ const imports = [
708
+ 'import type { APIRoute } from "astro";',
709
+ 'import { getSecret } from "astro:env/server";',
710
+ backend.kind === "gateway" ? 'import { createGateway, streamText } from "ai";' : 'import { streamText } from "ai";'
711
+ ];
712
+ let setup = "";
713
+ let modelExpr = JSON.stringify(backend.model);
714
+ if (backend.kind === "gateway") {
715
+ setup = `
716
+ const gateway = createGateway({ apiKey: getSecret("AI_GATEWAY_API_KEY") });
717
+ `;
718
+ modelExpr = `gateway(${JSON.stringify(backend.model)})`;
719
+ } else if (backend.kind === "openrouter") {
720
+ imports.push('import { createOpenRouter } from "@openrouter/ai-sdk-provider";');
721
+ setup = `
722
+ const openrouter = createOpenRouter({ apiKey: getSecret(${JSON.stringify(backend.apiKeyEnv)}) });
723
+ `;
724
+ modelExpr = `openrouter(${JSON.stringify(backend.model)})`;
725
+ } else if (backend.kind === "openai-compatible") {
726
+ imports.push('import { createOpenAICompatible } from "@ai-sdk/openai-compatible";');
727
+ setup = `
728
+ const provider = createOpenAICompatible({
729
+ apiKey: getSecret(${JSON.stringify(backend.apiKeyEnv)}),
730
+ baseURL: ${JSON.stringify(backend.baseUrl)},
731
+ name: ${JSON.stringify(backend.name)},
732
+ });
733
+ `;
734
+ modelExpr = `provider(${JSON.stringify(backend.model)})`;
735
+ }
736
+ if (grounded) {
737
+ imports.push('import { createAskContext } from "blume/ai/ask-context.ts";', 'import askData from "blume:ask-data";');
738
+ const groundFields = [];
739
+ if (instructions) {
740
+ groundFields.push(`instructions: ${JSON.stringify(instructions)}`);
741
+ }
742
+ if (options?.retrieval) {
743
+ groundFields.push(`retrieval: ${JSON.stringify(options.retrieval)}`);
744
+ }
745
+ const groundOptions = groundFields.length > 0 ? `, { ${groundFields.join(", ")} }` : "";
746
+ setup += `
747
+ const ground = createAskContext(askData${groundOptions});
748
+ `;
749
+ }
750
+ const validate = ` const body = await request.json().catch(() => null);
751
+ const raw = body?.messages;
752
+ const valid =
753
+ Array.isArray(raw) &&
754
+ raw.length > 0 &&
755
+ raw.length <= 40 &&
756
+ raw.every(
757
+ (m: unknown) =>
758
+ typeof m === "object" &&
759
+ m !== null &&
760
+ ("role" in m && (m.role === "user" || m.role === "assistant")) &&
761
+ ("content" in m && typeof m.content === "string")
762
+ ) &&
763
+ JSON.stringify(raw).length <= 24_000;
764
+ if (!valid) {
765
+ return new Response(
766
+ "Invalid request: send 1-40 user/assistant messages with string content.",
767
+ { status: 400 }
768
+ );
769
+ }
770
+ // Re-build the array so only role/content ever reach the model.
771
+ const messages = raw.map((m: { role: "user" | "assistant"; content: string }) => ({
772
+ content: m.content,
773
+ role: m.role,
774
+ }));`;
775
+ const keyCheck = backend.kind === "gateway" ? ` // The AI Gateway authenticates with an API key or Vercel's OIDC token.
776
+ if (!(getSecret("AI_GATEWAY_API_KEY") || getSecret("VERCEL_OIDC_TOKEN"))) {
777
+ return new Response(
778
+ "Ask AI is not configured: set AI_GATEWAY_API_KEY (or deploy on Vercel with OIDC).",
779
+ { status: 500 }
780
+ );
781
+ }` : ` if (!getSecret(${JSON.stringify(backend.apiKeyEnv)})) {
782
+ return new Response(
783
+ ${JSON.stringify(`Ask AI is not configured: set ${backend.apiKeyEnv}.`)},
784
+ { status: 500 }
785
+ );
786
+ }`;
787
+ const onError = ` onError({ error }) {
788
+ console.error("Ask AI provider error:", error);
789
+ },`;
790
+ const stream = grounded ? ` const instructions =
791
+ (await ground(messages, body.page)) ??
792
+ ${JSON.stringify(fallbackPrompt)};
793
+ const result = streamText({
794
+ model: ${modelExpr},
795
+ instructions,
796
+ messages,
797
+ ${onError}
798
+ });` : ` const result = streamText({
799
+ model: ${modelExpr},
800
+ instructions:
801
+ ${JSON.stringify(fallbackPrompt)},
802
+ messages,
803
+ ${onError}
804
+ });`;
805
+ const handler = `export const POST: APIRoute = async ({ request }) => {
806
+ ${validate}
807
+ ${keyCheck}
808
+ try {
809
+ ${stream}
810
+ return result.toTextStreamResponse();
811
+ } catch {
812
+ return new Response("Failed to generate a response.", { status: 500 });
813
+ }
814
+ };`;
815
+ return `// Generated by Blume. Do not edit.
816
+ ${imports.join(`
817
+ `)}
818
+
819
+ export const prerender = false;
820
+ ${setup}
821
+ ${handler}
822
+ `;
823
+ };
824
+ var askComponentTemplate = (askEnabled) => askEnabled ? `---
825
+ // Generated by Blume. Do not edit.
826
+ import AskAI from "blume/components/islands/AskAI.astro";
827
+ import data from "blume:data";
828
+
829
+ const { strings } = Astro.props;
830
+ ---
831
+
832
+ <AskAI
833
+ endpoint={data.config.ask?.endpoint ?? undefined}
834
+ strings={strings ?? data.ui.ask}
835
+ suggestions={data.config.ask?.suggestions ?? []}
836
+ />
837
+ ` : `---
838
+ // Generated by Blume. Do not edit.
839
+ // Ask AI is off (\`ai.ask.enabled\`), so the header's Ask trigger renders nothing.
840
+ // Deliberately imports no React island, keeping the JSX renderer out of projects
841
+ // that don't need it.
842
+ ---
843
+ `;
844
+ var searchEndpointTemplate = () => `// Generated by Blume. Do not edit.
845
+ import documents from "blume:search-index";
846
+
847
+ export const prerender = true;
848
+
849
+ export function GET() {
850
+ return new Response(JSON.stringify(documents), {
851
+ headers: { "Content-Type": "application/json" },
852
+ });
853
+ }
854
+ `;
855
+ var SEARCH_CLIENT_HEADER = `// Generated by Blume. Do not edit.
856
+ `;
857
+ var searchClientImport = (module) => `import { createSearch as create } from "blume/components/layout/search/${module}.ts";
858
+ `;
859
+ var SEARCH_BASE_IMPORT = `import { joinBase } from "blume/components/islands/base-path.ts";
860
+ `;
861
+ var staticSearchClient = (module, locale) => `${SEARCH_CLIENT_HEADER}${searchClientImport(module)}${SEARCH_BASE_IMPORT}
862
+ const indexUrl = joinBase(import.meta.env.BASE_URL, "blume-search.json");
863
+
864
+ export const createSearch = () => create({ indexUrl${locale ? `, locale: ${JSON.stringify(locale)}` : ""} });
865
+ `;
866
+ var hostedSearchClient = (module, options) => `${SEARCH_CLIENT_HEADER}${searchClientImport(module)}
867
+ export const createSearch = () => create(${JSON.stringify(options)});
868
+ `;
869
+ var hostedSearchOptions = (search) => {
870
+ switch (search.provider) {
871
+ case "algolia": {
872
+ return { module: "algolia", options: { ...search.algolia } };
873
+ }
874
+ case "orama-cloud": {
875
+ return {
876
+ module: "orama-cloud",
877
+ options: {
878
+ apiKey: search.oramaCloud?.apiKey,
879
+ endpoint: search.oramaCloud?.endpoint
880
+ }
881
+ };
882
+ }
883
+ case "typesense": {
884
+ return { module: "typesense", options: { ...search.typesense } };
885
+ }
886
+ default: {
887
+ return null;
888
+ }
889
+ }
890
+ };
891
+ var searchClientTemplate = (config) => {
892
+ const { search } = config;
893
+ if (search.provider === "orama" || search.provider === "flexsearch") {
894
+ const locale = search.provider === "orama" ? config.i18n?.defaultLocale : undefined;
895
+ return staticSearchClient(search.provider, locale);
896
+ }
897
+ const hosted = hostedSearchOptions(search);
898
+ if (hosted) {
899
+ return hostedSearchClient(hosted.module, hosted.options);
900
+ }
901
+ if (search.provider === "mixedbread") {
902
+ return `${SEARCH_CLIENT_HEADER}${searchClientImport("endpoint")}${SEARCH_BASE_IMPORT}
903
+ const api = joinBase(import.meta.env.BASE_URL, "api/search");
904
+
905
+ export const createSearch = () => create({ api });
906
+ `;
907
+ }
908
+ if (search.provider === "pagefind") {
909
+ return `${SEARCH_CLIENT_HEADER}${searchClientImport("pagefind")}${SEARCH_BASE_IMPORT}
910
+ const url = joinBase(import.meta.env.BASE_URL, "pagefind/pagefind.js");
911
+
912
+ export const createSearch = () => create({ url });
913
+ `;
914
+ }
915
+ return `${SEARCH_CLIENT_HEADER}export const createSearch = () => () =>
916
+ Promise.resolve({ hits: [], sections: [] });
917
+ `;
918
+ };
919
+ var mixedbreadSearchEndpointTemplate = (storeId) => `// Generated by Blume. Do not edit.
920
+ import type { APIRoute } from "astro";
921
+ import { getSecret } from "astro:env/server";
922
+ import Mixedbread from "@mixedbread/sdk";
923
+
924
+ export const prerender = false;
925
+
926
+ const client = new Mixedbread({ apiKey: getSecret("MIXEDBREAD_API_KEY") ?? "" });
927
+ const STORE_ID = ${JSON.stringify(storeId)};
928
+
929
+ export const POST: APIRoute = async ({ request }) => {
930
+ // The endpoint is public: a malformed body must 200-empty, not 500.
931
+ const body = await request.json().catch(() => null);
932
+ const query = body?.query;
933
+ if (!query || typeof query !== "string") {
934
+ return new Response("[]", {
935
+ headers: { "Content-Type": "application/json" },
936
+ });
937
+ }
938
+ const response = await client.stores.search({
939
+ query,
940
+ store_identifiers: [STORE_ID],
941
+ top_k: 8,
942
+ });
943
+ const hits = (response.data ?? []).map((chunk) => {
944
+ const meta = chunk.generated_metadata ?? {};
945
+ return {
946
+ excerpt: chunk.text ?? meta.excerpt ?? "",
947
+ title: meta.title ?? chunk.filename ?? "",
948
+ url: meta.url ?? "",
949
+ };
950
+ });
951
+ return new Response(JSON.stringify(hits), {
952
+ headers: { "Content-Type": "application/json" },
953
+ });
954
+ };
955
+ `;
956
+ var rawMarkdownEndpointTemplate = (kind) => `// Generated by Blume. Do not edit.
957
+ import raw from "blume:raw-markdown";
958
+
959
+ export const prerender = true;
960
+
961
+ export function getStaticPaths() {
962
+ return Object.keys(raw).map((route) => ({
963
+ params: { slug: route === "/" ? "index" : route.slice(1) },
964
+ props: { route },
965
+ }));
966
+ }
967
+
968
+ export function GET({ props }: { props: { route: string } }) {
969
+ const entries = raw as Record<string, { md?: string; mdx?: string }>;
970
+ const entry = entries[props.route];
971
+ const body = entry ? ${kind === "md" ? '(entry.md ?? entry.mdx ?? "")' : '(entry.mdx ?? "")'} : "";
972
+ return new Response(body, {
973
+ headers: {
974
+ "Content-Type": "text/markdown; charset=utf-8",
975
+ // ~4 characters per token; keep in sync with markdownTokenCount.
976
+ "x-markdown-tokens": String(Math.ceil(body.length / 4)),
977
+ },
978
+ });
979
+ }
980
+ `;
981
+ var contentAssetsEndpointTemplate = (stagedAssetsDir) => `// Generated by Blume. Do not edit.
982
+ import { existsSync } from "node:fs";
983
+ import { readdir, readFile } from "node:fs/promises";
984
+ import { isAbsolute, join, relative, resolve } from "node:path";
985
+ import type { APIRoute } from "astro";
986
+ import assets from "blume:content-assets";
987
+
988
+ export const prerender = true;
989
+
990
+ const files = assets as Record<string, string>;
991
+ const STAGED_DIR = ${JSON.stringify(stagedAssetsDir)};
992
+
993
+ const TYPES: Record<string, string> = {
994
+ ".apng": "image/apng",
995
+ ".avif": "image/avif",
996
+ ".bmp": "image/bmp",
997
+ ".gif": "image/gif",
998
+ ".ico": "image/x-icon",
999
+ ".jpeg": "image/jpeg",
1000
+ ".jpg": "image/jpeg",
1001
+ ".png": "image/png",
1002
+ ".svg": "image/svg+xml",
1003
+ ".tiff": "image/tiff",
1004
+ ".webp": "image/webp",
1005
+ };
1006
+
1007
+ const contentType = (path: string): string =>
1008
+ TYPES[path.slice(path.lastIndexOf(".")).toLowerCase()] ??
1009
+ "application/octet-stream";
1010
+
1011
+ const stagedParams = async (): Promise<string[]> => {
1012
+ if (!existsSync(STAGED_DIR)) {
1013
+ return [];
1014
+ }
1015
+ const entries = await readdir(STAGED_DIR, {
1016
+ recursive: true,
1017
+ withFileTypes: true,
1018
+ });
1019
+ return entries
1020
+ .filter((entry) => entry.isFile())
1021
+ .map((entry) =>
1022
+ relative(STAGED_DIR, join(entry.parentPath, entry.name)).replaceAll(
1023
+ "\\\\",
1024
+ "/"
1025
+ )
1026
+ );
1027
+ };
1028
+
1029
+ export async function getStaticPaths() {
1030
+ const params = [
1031
+ ...Object.keys(files).map((key) => "content/" + key),
1032
+ ...(await stagedParams()),
1033
+ ];
1034
+ return params.map((asset) => ({ params: { asset } }));
1035
+ }
1036
+
1037
+ const resolveAsset = (asset: string): string | null => {
1038
+ if (asset.startsWith("content/")) {
1039
+ return files[asset.slice("content/".length)] ?? null;
1040
+ }
1041
+ const abs = resolve(STAGED_DIR, asset);
1042
+ // Traversal guard: the dev server renders on demand, so the param is
1043
+ // attacker-controlled there — never step outside the staged directory.
1044
+ // path.relative rather than a string-prefix test: STAGED_DIR is baked in
1045
+ // with forward slashes while resolve() answers in the platform's
1046
+ // separators, so on Windows the prefix test 404'd every legitimate staged
1047
+ // asset — and a bare prefix also admits a sibling directory that merely
1048
+ // shares the name.
1049
+ const rel = relative(STAGED_DIR, abs);
1050
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
1051
+ return null;
1052
+ }
1053
+ return existsSync(abs) ? abs : null;
1054
+ };
1055
+
1056
+ export const GET: APIRoute = async ({ params }) => {
1057
+ const path = resolveAsset(String(params.asset ?? ""));
1058
+ if (!path) {
1059
+ return new Response(null, { status: 404 });
1060
+ }
1061
+ const body = await readFile(path);
1062
+ return new Response(new Uint8Array(body), {
1063
+ headers: { "Content-Type": contentType(path) },
1064
+ });
1065
+ };
1066
+ `;
1067
+ var mcpPageFile = (route) => `${trimChar(route, "/")}.ts`;
1068
+ var mcpEndpointTemplate = () => `// Generated by Blume. Do not edit.
1069
+ import type { APIRoute } from "astro";
1070
+ import { createMcpFetchHandler } from "blume/ai/mcp/server.ts";
1071
+ import data from "blume:mcp-data";
1072
+
1073
+ export const prerender = false;
1074
+
1075
+ const handler = createMcpFetchHandler(data);
1076
+
1077
+ export const ALL: APIRoute = ({ request }) => handler(request);
1078
+ `;
1079
+ var playgroundProxyTemplate = (origins) => `// Generated by Blume. Do not edit.
1080
+ import type { APIRoute } from "astro";
1081
+ import { createPlaygroundProxyHandler } from "blume/openapi/proxy.ts";
1082
+
1083
+ export const prerender = false;
1084
+
1085
+ const handler = createPlaygroundProxyHandler(${JSON.stringify(origins)});
1086
+
1087
+ export const ALL: APIRoute = ({ request }) => handler(request);
1088
+ `;
1089
+ var staticJsonEndpointTemplate = (payload) => `// Generated by Blume. Do not edit.
1090
+ export const prerender = true;
1091
+
1092
+ const payload = ${JSON.stringify(payload, null, 2)};
1093
+
1094
+ export function GET() {
1095
+ return new Response(JSON.stringify(payload), {
1096
+ headers: { "Content-Type": "application/json" },
1097
+ });
1098
+ }
1099
+ `;
1100
+ var rssEndpointTemplate = () => `// Generated by Blume. Do not edit.
1101
+ import feeds from "blume:rss";
1102
+
1103
+ export const prerender = true;
1104
+
1105
+ export function getStaticPaths() {
1106
+ return Object.keys(feeds).map((section) => ({
1107
+ params: { section },
1108
+ props: { section },
1109
+ }));
1110
+ }
1111
+
1112
+ export function GET({ props }: { props: { section: string } }) {
1113
+ const bySection = feeds as Record<string, string>;
1114
+ return new Response(bySection[props.section] ?? "", {
1115
+ headers: { "Content-Type": "application/rss+xml; charset=utf-8" },
1116
+ });
1117
+ }
1118
+ `;
1119
+ var ogEndpointTemplate = (customRoutes = [], og = {}, includeChangelog = false) => `// Generated by Blume. Do not edit.
1120
+ import { renderOgImage } from "blume/og";
1121
+ import type { OgFont, OgFontFamilies } from "blume/og";
1122
+ import data from "blume:data";
1123
+
1124
+ export const prerender = true;
1125
+
1126
+ // Custom (non-content) pages opted into a generated card, baked in at build.
1127
+ // The annotation keeps the empty-array case from being an implicit any[]
1128
+ // (ts(7034)) under a strict tsconfig.
1129
+ const customRoutes: { slug: string; title: string }[] = ${JSON.stringify(customRoutes)};
1130
+
1131
+ // Card fonts, resolved at generation time (explicit seo.og.fonts, or derived
1132
+ // from theme.fonts). Local entries carry absolute build-machine paths, which
1133
+ // is why they are baked into this build-only endpoint instead of the runtime
1134
+ // data that pages serialize into HTML.
1135
+ const fonts: OgFont[] = ${JSON.stringify(og.fonts ?? [])};
1136
+ const families: OgFontFamilies | undefined = ${og.families ? JSON.stringify(og.families) : "undefined"};
1137
+
1138
+ // A page's own description (its \`seo.description\`, else \`description\`) is
1139
+ // the card subtitle, so the image says what the page's og:description says.
1140
+ // Pages without one fall back to the site-wide subtitle at render time.
1141
+ // \`seo.og.description: false\` hides the subtitle on every card, page text
1142
+ // included, which is what switches this off.
1143
+ const pageDescriptions = ${og.pageDescriptions !== false};
1144
+
1145
+ interface CardProps {
1146
+ title: string;
1147
+ description: string | null;
1148
+ }
1149
+
1150
+ export function getStaticPaths() {
1151
+ const seen = new Set<string>();
1152
+ const paths: { params: { slug: string }; props: CardProps }[] = [];
1153
+ const add = (slug: string, title: string, description: string | null) => {
1154
+ if (seen.has(slug)) {
1155
+ return;
1156
+ }
1157
+ seen.add(slug);
1158
+ paths.push({
1159
+ params: { slug },
1160
+ props: { title, description: pageDescriptions ? description : null },
1161
+ });
1162
+ };
1163
+ // A custom page wins over a content route sharing its path, so add it first.
1164
+ // Its description is unknown at generate time, so it takes the site subtitle.
1165
+ for (const route of customRoutes) {
1166
+ add(route.slug, route.title, null);
1167
+ }
1168
+ for (const route of data.routes) {
1169
+ add(
1170
+ route.path === "/" ? "index" : route.path.slice(1),
1171
+ route.title,
1172
+ route.description
1173
+ );
1174
+ }${includeChangelog ? `
1175
+ // The generated changelog index is not a content route, so it needs its own
1176
+ // card. Added last: a custom page or content route owning /changelog wins.
1177
+ add(
1178
+ "changelog",
1179
+ data.ui.changelog?.title ?? "Changelog",
1180
+ data.ui.changelog?.description ?? null
1181
+ );` : ""}
1182
+ return paths;
1183
+ }
1184
+
1185
+ // Footer branding shared by every card. The slug comes from the configured
1186
+ // repo rather than the URL, so an Enterprise host reads the same as github.com;
1187
+ // the site text (host plus deployment base) is resolved at generate time.
1188
+ const repoSlug = data.config.github
1189
+ ? \`\${data.config.github.owner}/\${data.config.github.repo}\`
1190
+ : undefined;
1191
+
1192
+ export async function GET({ props }: { props: CardProps }) {
1193
+ const png = await renderOgImage({
1194
+ accent: data.config.og.palette?.accent ?? data.config.theme.accent.light,
1195
+ brand: data.config.title,
1196
+ description: props.description ?? data.config.og.description,
1197
+ families,
1198
+ fonts,
1199
+ logo: data.config.og.logo,
1200
+ palette: data.config.og.palette,
1201
+ repo: repoSlug,
1202
+ site: data.config.og.site,
1203
+ title: props.title,
1204
+ });
1205
+ return new Response(new Uint8Array(png), {
1206
+ headers: {
1207
+ "Cache-Control": "public, max-age=31536000, immutable",
1208
+ "Content-Type": "image/png",
1209
+ },
1210
+ });
1211
+ }
1212
+ `;
1213
+ var scalarReferenceTemplate = (options) => `---
1214
+ // Generated by Blume. Do not edit.
1215
+ import { ScalarComponent } from "@scalar/astro";
1216
+ import ReferenceLayout from "blume/components/layout/ReferenceLayout.astro";
1217
+ import data from "blume:data";
1218
+
1219
+ export const prerender = true;
1220
+
1221
+ const configuration = ${JSON.stringify(options.configuration, null, 2)};
1222
+
1223
+ // The reference is an unlocalized route, so its chrome renders in the default
1224
+ // locale's language and direction (\`data.ui\` is the default locale's resolved
1225
+ // dictionary), mirroring the changelog index's locale wiring.
1226
+ const i18n = data.config.i18n;
1227
+ const localeMeta = i18n
1228
+ ? i18n.locales.find((l) => l.code === i18n.defaultLocale)
1229
+ : null;
1230
+ const dir = localeMeta?.dir ?? "ltr";
1231
+ const htmlLang = i18n ? i18n.defaultLocale : "en";
1232
+ ---
1233
+
1234
+ <ReferenceLayout
1235
+ analytics={data.config.analytics}
1236
+ banner={data.config.banner}
1237
+ dir={dir}
1238
+ fontCssVars={data.fontCssVars}
1239
+ locale={htmlLang}
1240
+ logo={data.config.logo}
1241
+ favicon={data.config.favicon}
1242
+ appleIcon={data.config.appleIcon}
1243
+ navigation={data.navigation}
1244
+ noindex={${options.noindex === true}}
1245
+ pageTitle={${JSON.stringify(options.title)}}
1246
+ route={${JSON.stringify(options.route)}}
1247
+ searchEnabled={data.config.search.enabled}
1248
+ site={{ title: data.config.title, description: data.config.description }}
1249
+ themeMode={data.config.theme.mode}
1250
+ ui={data.ui}
1251
+ >
1252
+ <ScalarComponent configuration={configuration} renderMode="client" />
1253
+ </ReferenceLayout>
1254
+ `;
1255
+ var catchAllPageTemplate = (options) => {
1256
+ const mathImport = options.mathEnabled ? `import Math from "blume/components/content/Math.astro";
1257
+ ` : "";
1258
+ const mathEntry = options.mathEnabled ? `Math,
1259
+ ` : "";
1260
+ const clientData = options.needsReact ? `
1261
+ clientData={{ config: data.config, navigation, page: { route, title: seo.title ?? title } }}` : "";
1262
+ return `---
1263
+ // Generated by Blume. Do not edit.
1264
+ import { getEntry, render } from "astro:content";
1265
+ import type { CollectionKey } from "astro:content";
1266
+ import RootLayout from "blume/components/layout/RootLayout.astro";
1267
+ import { withBase } from "blume/components/islands/base-path.ts";
1268
+ import { stripBasePath, withBasePath } from "blume/core/base-path.ts";
1269
+ import { resolveSlot } from "blume/components/layout/overrides.ts";
1270
+ import Accordion from "blume/components/content/Accordion.astro";
1271
+ import AccordionItem from "blume/components/content/AccordionItem.astro";
1272
+ import AutoTypeTable from "blume/components/content/AutoTypeTable.astro";
1273
+ import Badge from "blume/components/content/Badge.astro";
1274
+ import Callout from "blume/components/content/Callout.astro";
1275
+ import Card from "blume/components/content/Card.astro";
1276
+ import CardGroup from "blume/components/content/CardGroup.astro";
1277
+ import CodeBlock from "blume/components/content/CodeBlock.astro";
1278
+ import CodeGroup from "blume/components/content/CodeGroup.astro";
1279
+ import ColorRoot from "blume/components/content/Color.astro";
1280
+ import ColorItem from "blume/components/content/ColorItem.astro";
1281
+ import ColorRow from "blume/components/content/ColorRow.astro";
1282
+ import Column from "blume/components/content/Column.astro";
1283
+ import Columns from "blume/components/content/Columns.astro";
1284
+ import Component from "blume/components/content/Component.astro";
1285
+ import Diff from "blume/components/content/Diff.astro";
1286
+ import Expandable from "blume/components/content/Expandable.astro";
1287
+ import FileTree from "blume/components/content/FileTree.astro";
1288
+ import Frame from "blume/components/content/Frame.astro";
1289
+ import GithubInfo from "blume/components/content/GithubInfo.astro";
1290
+ import Panel from "blume/components/content/Panel.astro";
1291
+ import Prompt from "blume/components/content/Prompt.astro";
1292
+ import Step from "blume/components/content/Step.astro";
1293
+ import Steps from "blume/components/content/Steps.astro";
1294
+ import Tab from "blume/components/content/Tab.astro";
1295
+ import Tabs from "blume/components/content/Tabs.astro";
1296
+ import Tile from "blume/components/content/Tile.astro";
1297
+ import Tooltip from "blume/components/content/Tooltip.astro";
1298
+ import TreeRoot from "blume/components/content/Tree.astro";
1299
+ import TreeFile from "blume/components/content/TreeFile.astro";
1300
+ import TreeFolder from "blume/components/content/TreeFolder.astro";
1301
+ import TypeTable from "blume/components/content/TypeTable.astro";
1302
+ import Visibility from "blume/components/content/Visibility.astro";
1303
+ import YouTube from "blume/components/content/YouTube.astro";
1304
+ import Icon from "blume/components/Icon.astro";
1305
+ import LocaleLinks from "blume/components/layout/LocaleLinks.astro";
1306
+ import ApiOverview from "blume/components/openapi/ApiOverview.astro";
1307
+ import ApiTagOperations from "blume/components/openapi/ApiTagOperations.astro";
1308
+ import Operation from "blume/components/openapi/Operation.astro";
1309
+ ${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../generated/components.ts";
1310
+ import { islandComponents } from "../generated/islands.ts";
1311
+ import data from "blume:data";
1312
+
1313
+ const Color = Object.assign(ColorRoot, { Item: ColorItem, Row: ColorRow });
1314
+ const Tree = Object.assign(TreeRoot, { File: TreeFile, Folder: TreeFolder });
1315
+
1316
+ // Docs content is file-based and always prerendered, even in server output
1317
+ // (where only endpoints like /api/ask render on demand). Without this, server
1318
+ // builds would render this route on demand and ignore getStaticPaths, leaving
1319
+ // the entry id undefined.
1320
+ export const prerender = true;
1321
+
1322
+ const components = {
1323
+ Accordion,
1324
+ AccordionItem,
1325
+ ApiOverview,
1326
+ ApiTagOperations,
1327
+ AutoTypeTable,
1328
+ Badge,
1329
+ Callout,
1330
+ Card,
1331
+ CardGroup,
1332
+ CodeBlock,
1333
+ CodeGroup,
1334
+ Color,
1335
+ Column,
1336
+ Columns,
1337
+ Component,
1338
+ Diff,
1339
+ Expandable,
1340
+ FileTree,
1341
+ Frame,
1342
+ GithubInfo,
1343
+ Icon,
1344
+ Operation,
1345
+ Panel,
1346
+ Prompt,
1347
+ Step,
1348
+ Steps,
1349
+ Tab,
1350
+ Tabs,
1351
+ Tile,
1352
+ Tooltip,
1353
+ Tree,
1354
+ TypeTable,
1355
+ Visibility,
1356
+ YouTube,
1357
+ ${mathEntry}...islandComponents,
1358
+ ...userMdx,
1359
+ };
1360
+
1361
+ export function getStaticPaths() {
1362
+ return data.routes.map((route) => ({
1363
+ params: { slug: route.path === "/" ? undefined : route.path.slice(1) },
1364
+ props: {
1365
+ alternates: route.alternates,
1366
+ collection: route.collection,
1367
+ editUrl: route.editUrl,
1368
+ entryId: route.entryId,
1369
+ fallback: route.fallback,
1370
+ indexable: route.indexable,
1371
+ lastModified: route.lastModified,
1372
+ locale: route.locale,
1373
+ route: route.path,
1374
+ title: route.title,
1375
+ version: route.version,
1376
+ versionAlternates: route.versionAlternates,
1377
+ },
1378
+ }));
1379
+ }
1380
+
1381
+ const { entryId, collection, route, title, indexable, editUrl, lastModified, locale, alternates, fallback, version, versionAlternates } = Astro.props;
1382
+ const entry = await getEntry(collection as CollectionKey, entryId);
1383
+ if (!entry) {
1384
+ return new Response(null, { status: 404 });
1385
+ }
1386
+ const { Content, headings: allHeadings, remarkPluginFrontmatter } = await render(entry);
1387
+ // \`[!toc]\`-marked headings render on the page but stay out of the table of
1388
+ // contents; the heading plugin reports their slugs through the render's
1389
+ // frontmatter (see markdown/heading-anchors.ts). Only the plugin's array
1390
+ // counts: \`frontmatter.extend\` can declare the same key, and on a page with
1391
+ // no headings that user-supplied value would pass straight through.
1392
+ const tocHiddenRaw = remarkPluginFrontmatter?.${TOC_HIDDEN_KEY};
1393
+ const tocHidden = new Set(Array.isArray(tocHiddenRaw) ? tocHiddenRaw : []);
1394
+ const headings =
1395
+ tocHidden.size > 0
1396
+ ? allHeadings.filter((heading) => !tocHidden.has(heading.slug))
1397
+ : allHeadings;
1398
+ const frontmatter = entry.data;
1399
+
1400
+ const seo = frontmatter.seo;
1401
+ const base = data.config.site ? data.config.site.replace(/\\/$/, "") : null;
1402
+
1403
+ // Percent-encode the route-derived path (the sitemap convention): a Unicode
1404
+ // slug (\`/api/größe\`) is not legal in a raw URI, and crawlers compare
1405
+ // canonical against the sitemap's encoded <loc> byte-for-byte.
1406
+ const ogPath = data.config.og.enabled
1407
+ ? encodeURI(\`/og/\${route === "/" ? "index" : route.slice(1)}.png\`)
1408
+ : null;
1409
+ const ogRel = seo.image ?? ogPath;
1410
+ // Absolute URLs also carry the deployment base (the page is served under it):
1411
+ // \`site + base + path\`. Only absolutize root-relative paths: \`seo.image\` may be
1412
+ // an external URL, which passes through verbatim (mirrors PageLayout).
1413
+ const ogImage =
1414
+ ogRel && base && ogRel.startsWith("/") ? \`\${base}\${withBase(ogRel)}\` : ogRel;
1415
+ // Blume's generated card has known dimensions the layout can declare; a user's
1416
+ // \`seo.image\` could be any size or format, so it gets none.
1417
+ const ogGenerated = !seo.image && Boolean(ogPath);
1418
+
1419
+ // X attribution: the site's account, plus a creator the page can claim for
1420
+ // itself (a guest post crediting its own author) over the configured default.
1421
+ const x = { ...data.config.x, ...(seo.x?.creator ? { creator: seo.x.creator } : {}) };
1422
+
1423
+ const basedRoute = withBase(route);
1424
+
1425
+ // Locale resolution. With i18n on, pick the active locale's nav + dictionary,
1426
+ // build hreflang alternates, and derive the language-switcher targets.
1427
+ const i18n = data.config.i18n;
1428
+ const localePrefix = (codeArg: string) =>
1429
+ i18n && codeArg === i18n.defaultLocale && i18n.hideDefaultLocalePrefix
1430
+ ? ""
1431
+ : \`/\${codeArg}\`;
1432
+ const localizeRoute = (logical: string, codeArg: string) => {
1433
+ const prefix = localePrefix(codeArg);
1434
+ if (!prefix) {
1435
+ return logical;
1436
+ }
1437
+ return logical === "/" ? prefix : \`\${prefix}\${logical}\`;
1438
+ };
1439
+ const stripLocale = (path: string, codeArg: string) => {
1440
+ const prefix = localePrefix(codeArg);
1441
+ return prefix && path.startsWith(prefix) ? path.slice(prefix.length) || "/" : path;
1442
+ };
1443
+
1444
+ // Version resolution. An archived page renders its snapshot's navigation tree,
1445
+ // points its canonical at the latest equivalent (unless configured otherwise),
1446
+ // and shows the old-version notice.
1447
+ const versionsConfig = data.config.versions;
1448
+ const archived = versionsConfig && version
1449
+ ? (versionsConfig.archived.find((v) => v.id === version) ?? null)
1450
+ : null;
1451
+ const latestVersionAlt = (versionAlternates ?? []).find((alt) => alt.version === "");
1452
+
1453
+ const navigation = version
1454
+ ? (data.navigationByVersion[version]?.[i18n ? locale : ""] ?? data.navigation)
1455
+ : i18n
1456
+ ? (data.navigationByLocale[locale] ?? data.navigation)
1457
+ : data.navigation;
1458
+ const ui = i18n ? (data.uiByLocale[locale] ?? data.ui) : data.ui;
1459
+ const localeMeta = i18n ? i18n.locales.find((l) => l.code === locale) : null;
1460
+ const dir = localeMeta?.dir ?? "ltr";
1461
+ const htmlLang = i18n ? locale : "en";
1462
+ // A fallback page renders the fallback locale's content, so its text direction
1463
+ // follows that language — not the (mirrored) page locale.
1464
+ const contentLocale =
1465
+ fallback && i18n?.fallbackLocale ? i18n.fallbackLocale : locale;
1466
+ const contentDir = i18n
1467
+ ? (i18n.locales.find((l) => l.code === contentLocale)?.dir ?? "ltr")
1468
+ : "ltr";
1469
+ // The root route keeps its trailing slash (\`https://site/\`) so canonical and
1470
+ // hreflang URLs byte-match the sitemap's <loc> for the home page.
1471
+ const absolute = (path: string) => base + withBase(path);
1472
+
1473
+ // An archived page defaults its canonical to the same page in the latest docs
1474
+ // when that page still exists — search engines treat the live page as
1475
+ // authoritative without deindexing version-only content. A page's own
1476
+ // \`seo.canonical\` always wins, and \`canonical: "self"\` keeps the default.
1477
+ const canonical =
1478
+ seo.canonical ??
1479
+ (archived && archived.canonical === "latest" && latestVersionAlt && base
1480
+ ? absolute(latestVersionAlt.path)
1481
+ : base
1482
+ ? \`\${base}\${basedRoute === "/" ? "/" : encodeURI(basedRoute)}\`
1483
+ : null);
1484
+ const effectiveNoindex = Boolean(seo.noindex) || (archived?.noindex ?? false);
1485
+
1486
+ const localeAlternates =
1487
+ i18n && base
1488
+ ? (alternates ?? []).map((alt) => ({ hreflang: alt.locale, href: absolute(alt.path) }))
1489
+ : [];
1490
+ const defaultAlt = i18n ? (alternates ?? []).find((alt) => alt.locale === i18n.defaultLocale) : null;
1491
+ const xDefault = defaultAlt && base ? absolute(defaultAlt.path) : null;
1492
+
1493
+ // \`route\` arrives with \`basePath\` already applied, so the locale segment it
1494
+ // carries sits *after* the base (\`/docs/ja/guide\`), while \`localePrefix\` is
1495
+ // base-less (\`/ja\`). Stripping and re-adding a locale therefore happen in
1496
+ // base-less space, with the base re-applied at the end — the same
1497
+ // \`withBasePath(basePath, localizeRoute(...))\` composition the manifest uses to
1498
+ // build every real route. Done in based space, \`stripLocale\` matches nothing
1499
+ // and \`localizeRoute\` prepends a second prefix (\`/ja/docs/ja/guide\`). Only a
1500
+ // switcher entry for a locale with no real translation reaches this fallback:
1501
+ // a partially translated hand-written page for its missing locales, or a
1502
+ // generated reference (which has no \`alternates\` at all) for every locale
1503
+ // but its own.
1504
+ const mountLocalized = (logical: string, codeArg: string) =>
1505
+ withBasePath(data.config.basePath, localizeRoute(logical, codeArg));
1506
+ const logicalRoute = i18n
1507
+ ? stripLocale(stripBasePath(data.config.basePath, route), locale)
1508
+ : route;
1509
+ const localeSwitch = i18n
1510
+ ? i18n.locales.map((l) => {
1511
+ const alt = (alternates ?? []).find((x) => x.locale === l.code);
1512
+ return {
1513
+ code: l.code,
1514
+ current: l.code === locale,
1515
+ dir: l.dir,
1516
+ href: alt ? alt.path : mountLocalized(logicalRoute, l.code),
1517
+ label: l.label,
1518
+ untranslated: !alt,
1519
+ };
1520
+ })
1521
+ : [];
1522
+
1523
+ // Version switcher + old-version notice. The switcher auto-populates from the
1524
+ // versions config as a \`kind: "version"\` selector; a user-declared version
1525
+ // selector in \`navigation.selectors\` suppresses it (theirs renders instead).
1526
+ // Fallback version roots compose like real routes — \`{basePath}/{locale?}/{id}\`
1527
+ // (manifest \`versionAlternates\` paths arrive with the base already applied).
1528
+ const versionRootFor = (id: string) => {
1529
+ const logical = id ? \`/\${id}\` : "/";
1530
+ return i18n
1531
+ ? mountLocalized(logical, locale)
1532
+ : withBasePath(data.config.basePath, logical);
1533
+ };
1534
+ const samePageSwitch = versionsConfig
1535
+ ? versionsConfig.switcher.redirect === "same-page"
1536
+ : true;
1537
+ const userHasVersionSelector = navigation.selectors.some(
1538
+ (selector) => selector.kind === "version"
1539
+ );
1540
+ const versionSelector =
1541
+ versionsConfig && !userHasVersionSelector
1542
+ ? {
1543
+ items: [
1544
+ {
1545
+ id: "",
1546
+ label: versionsConfig.current.label,
1547
+ tag: versionsConfig.current.badge,
1548
+ },
1549
+ ...versionsConfig.archived.map((v) => ({
1550
+ id: v.id,
1551
+ label: v.label ?? v.id,
1552
+ tag: undefined,
1553
+ })),
1554
+ ].map((entry) => {
1555
+ const alt = (versionAlternates ?? []).find(
1556
+ (a) => a.version === entry.id
1557
+ );
1558
+ return {
1559
+ label: entry.label,
1560
+ path: samePageSwitch && alt ? alt.path : versionRootFor(entry.id),
1561
+ ...(entry.tag ? { tag: entry.tag } : {}),
1562
+ };
1563
+ }),
1564
+ kind: "version" as const,
1565
+ label: ui.versions.switcher,
1566
+ }
1567
+ : null;
1568
+
1569
+ const versionNotice =
1570
+ archived && archived.banner !== false
1571
+ ? {
1572
+ latestHref: latestVersionAlt
1573
+ ? latestVersionAlt.path
1574
+ : versionRootFor(""),
1575
+ latestLabel: ui.versions.latest,
1576
+ message:
1577
+ typeof archived.banner === "string"
1578
+ ? archived.banner
1579
+ : ui.versions.notice.replace(
1580
+ "{version}",
1581
+ archived.label ?? archived.id
1582
+ ),
1583
+ }
1584
+ : null;
1585
+
1586
+ // The whole page shell is overridable via \`layout.Layout\`; it receives the same
1587
+ // props as the built-in RootLayout, plus the \`layout\` map for its inner slots.
1588
+ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
1589
+ ---
1590
+
1591
+ <LayoutComponent
1592
+ site={{ title: data.config.title, description: data.config.description }}
1593
+ layout={layoutOverrides}${clientData}
1594
+ logo={data.config.logo}
1595
+ mcp={data.config.mcp}
1596
+ favicon={data.config.favicon}
1597
+ appleIcon={data.config.appleIcon}
1598
+ banner={data.config.banner}
1599
+ analytics={data.config.analytics}
1600
+ imageZoom={data.config.imageZoom}
1601
+ codeWrap={data.config.codeWrap}
1602
+ navigation={navigation}
1603
+ locale={htmlLang}
1604
+ dir={dir}
1605
+ contentDir={contentDir}
1606
+ ui={ui}
1607
+ localeAlternates={localeAlternates}
1608
+ xDefault={xDefault}
1609
+ localeSwitch={localeSwitch}
1610
+ versionSelector={versionSelector}
1611
+ versionNotice={versionNotice}
1612
+ searchVersion={versionsConfig ? version : null}
1613
+ page={{ title: seo.title ?? title, description: seo.description ?? frontmatter.description, route }}
1614
+ headings={headings}
1615
+ toc={data.config.toc}
1616
+ dateFormat={data.config.dateFormat}
1617
+ themeMode={data.config.theme.mode}
1618
+ fontCssVars={data.fontCssVars}
1619
+ searchEnabled={data.config.search.enabled}
1620
+ indexable={indexable}
1621
+ ogImage={ogImage}
1622
+ ogGenerated={ogGenerated}
1623
+ x={x}
1624
+ canonical={canonical}
1625
+ editUrl={editUrl}
1626
+ feedback={data.config.feedback}
1627
+ exportPdf={${options.exportPdf}}
1628
+ exportEpub={${options.exportEpub}}
1629
+ openInChat={data.config.openInChat}
1630
+ feeds={data.feeds}
1631
+ discovery={data.config.discovery}
1632
+ siteUrl={data.config.site}
1633
+ pageType={frontmatter.type}
1634
+ published={frontmatter.date ?? frontmatter.changelog?.date ?? null}
1635
+ lastModified={lastModified}
1636
+ noindex={effectiveNoindex}
1637
+ structuredDataEnabled={data.config.structuredData}
1638
+ >
1639
+ <h1>{title}</h1>
1640
+ {frontmatter.description && <p class="text-lg text-muted-foreground">{frontmatter.description}</p>}
1641
+ <LocaleLinks locale={locale}>
1642
+ <Content components={components} />
1643
+ </LocaleLinks>
1644
+ </LayoutComponent>
1645
+ `;
1646
+ };
1647
+ var changelogIndexTemplate = (options) => {
1648
+ const clientData = options.needsReact ? `
1649
+ clientData={{ config: data.config, navigation: data.navigation, page: { route: "/changelog", title: pageTitle } }}` : "";
1650
+ const stagedSpread = options.staged ? `
1651
+ ...(await getCollection("staged")),` : "";
1652
+ return `---
1653
+ // Generated by Blume. Do not edit.
1654
+ import { getCollection, render } from "astro:content";
1655
+ import RootLayout from "blume/components/layout/RootLayout.astro";
1656
+ import Update from "blume/components/content/Update.astro";
1657
+ import { withBase } from "blume/components/islands/base-path.ts";
1658
+ import { resolveSlot } from "blume/components/layout/overrides.ts";
1659
+ import { resolveDateFormatOptions } from "blume/core/date-format.ts";
1660
+ import { layoutOverrides } from "../generated/components.ts";
1661
+ import data from "blume:data";
1662
+
1663
+ export const prerender = true;
1664
+
1665
+ const entryDate = (entry: {
1666
+ data: { date?: string | null; changelog?: { date?: string | null } | null };
1667
+ }) => entry.data.date ?? entry.data.changelog?.date ?? null;
1668
+
1669
+ const toTime = (value: string | null | undefined) => {
1670
+ if (!value) {
1671
+ return 0;
1672
+ }
1673
+ const date = new Date(value);
1674
+ return Number.isNaN(date.getTime()) ? 0 : date.getTime();
1675
+ };
1676
+
1677
+ // The changelog is an unlocalized route, so its chrome renders in the default
1678
+ // locale's dictionary and direction (\`data.ui\` is the default locale's resolved
1679
+ // dictionary), mirroring the catch-all's locale wiring.
1680
+ const i18n = data.config.i18n;
1681
+ const localeMeta = i18n
1682
+ ? i18n.locales.find((l) => l.code === i18n.defaultLocale)
1683
+ : null;
1684
+ const dir = localeMeta?.dir ?? "ltr";
1685
+ const htmlLang = i18n ? i18n.defaultLocale : "en";
1686
+
1687
+ // Formatted in the same locale as the chrome, and with the configured
1688
+ // \`dateFormat\` (UTC by default), to match the per-page "last updated" stamp.
1689
+ const dateFormatOptions = resolveDateFormatOptions(data.config.dateFormat);
1690
+ const formatDate = (value: string | null | undefined) => {
1691
+ if (!value) {
1692
+ return;
1693
+ }
1694
+ const date = new Date(value);
1695
+ return Number.isNaN(date.getTime())
1696
+ ? undefined
1697
+ : new Intl.DateTimeFormat(htmlLang, dateFormatOptions).format(date);
1698
+ };
1699
+
1700
+ const slugify = (text: string) =>
1701
+ text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") ||
1702
+ "update";
1703
+
1704
+ // The major of a version's embedded semver (\`1.2.3\` -> 1, \`pkg@2.0.0\` -> 2), or
1705
+ // null when there is no full major.minor.patch to key on. Drives the changelog's
1706
+ // group-by-major pagination, so it tolerates the scoped tags monorepos publish.
1707
+ const majorVersion = (version: string | null | undefined) => {
1708
+ const match = /(\\d+)\\.\\d+\\.\\d+/.exec(String(version ?? ""));
1709
+ return match ? Number(match[1]) : null;
1710
+ };
1711
+
1712
+ // Map each entry to its own generated page so the timeline heading can deep-link
1713
+ // to it. The collection entry id matches the route manifest's \`entryId\`.
1714
+ const routeByEntry = new Map(
1715
+ data.routes.map((route) => [route.entryId, route.path])
1716
+ );
1717
+
1718
+ const changelogEntries = [
1719
+ ...(await getCollection("docs")),${stagedSpread}
1720
+ ]
1721
+ .filter(
1722
+ (entry) =>
1723
+ entry.data.type === "changelog" &&
1724
+ !entry.data.draft &&
1725
+ !entry.data.sidebar?.hidden
1726
+ )
1727
+ .toSorted((a, b) => toTime(entryDate(b)) - toTime(entryDate(a)));
1728
+
1729
+ const items = await Promise.all(
1730
+ changelogEntries.map(async (entry) => {
1731
+ const label =
1732
+ entry.data.title ??
1733
+ (entry.data.changelog?.version
1734
+ ? "v" + entry.data.changelog.version
1735
+ : "Update");
1736
+ return {
1737
+ Content: (await render(entry)).Content,
1738
+ date: formatDate(entryDate(entry)),
1739
+ href: routeByEntry.get(entry.id) ?? undefined,
1740
+ id: slugify(label),
1741
+ label,
1742
+ major: majorVersion(entry.data.changelog?.version),
1743
+ tags: entry.data.changelog?.category
1744
+ ? [entry.data.changelog.category]
1745
+ : [],
1746
+ };
1747
+ })
1748
+ );
1749
+
1750
+ // Repeated labels slug to the same id (e.g. two entries with neither a title
1751
+ // nor a version both falling back to "update"); suffix the later ones -2, -3,
1752
+ // ... so every heading deep-links to its own entry. The first keeps the plain
1753
+ // slug, and the rendered ids stay in lockstep with the \`headings\` list below.
1754
+ const seenIds = new Set();
1755
+ for (const item of items) {
1756
+ let uniqueId = item.id;
1757
+ for (let n = 2; seenIds.has(uniqueId); n += 1) {
1758
+ uniqueId = item.id + "-" + n;
1759
+ }
1760
+ seenIds.add(uniqueId);
1761
+ item.id = uniqueId;
1762
+ }
1763
+
1764
+ // A changelog is semver-paginated only when every visible release parses as
1765
+ // semver and they span more than one major line. Older majors then collapse
1766
+ // into groups the reader reveals one at a time; otherwise the timeline is flat.
1767
+ const majors = items.every((item) => item.major !== null)
1768
+ ? [...new Set(items.map((item) => item.major))]
1769
+ .filter((major): major is number => major !== null)
1770
+ .toSorted((a, b) => b - a)
1771
+ : [];
1772
+ const paginate = majors.length > 1;
1773
+ const majorGroups = majors.map((major) => ({
1774
+ items: items.filter((item) => item.major === major),
1775
+ label: major + ".x",
1776
+ major,
1777
+ }));
1778
+
1779
+ const headings = items.map((item) => ({
1780
+ depth: 2,
1781
+ slug: item.id,
1782
+ text: item.label,
1783
+ }));
1784
+
1785
+ const base = data.config.site ? data.config.site.replace(/\\/$/, "") : null;
1786
+ // The canonical URL carries the deployment base (the page is served under it),
1787
+ // matching how the catch-all canonicalizes via \`withBase(route)\`.
1788
+ const basedRoute = withBase("/changelog");
1789
+ const canonical = base ? base + basedRoute : null;
1790
+
1791
+ // The generated OG card for this route (the /og endpoint emits it alongside
1792
+ // the content-route cards), absolutized like the catch-all's so crawlers get
1793
+ // a full URL when the site is known.
1794
+ const ogPath = data.config.og.enabled ? withBase("/og/changelog.png") : null;
1795
+ const ogImage = ogPath && base ? base + ogPath : ogPath;
1796
+
1797
+ // The page chrome (h1, title, description) comes from the same translatable
1798
+ // \`changelog\` group as the reveal button; optional chaining tolerates a
1799
+ // not-yet-regenerated data snapshot from before these keys existed.
1800
+ const changelogTitle = data.ui.changelog?.title ?? "Changelog";
1801
+ const changelogDescription =
1802
+ data.ui.changelog?.description ??
1803
+ "Product updates, new features, and fixes from every release.";
1804
+ // The layout suffixes "- {site title}" itself, so the page title is just the
1805
+ // changelog's own name — prefixing the site title too would double it
1806
+ // ("Acme Changelog - Acme").
1807
+ const pageTitle = changelogTitle;
1808
+
1809
+ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
1810
+ ---
1811
+
1812
+ <LayoutComponent
1813
+ site={{ title: data.config.title, description: data.config.description }}
1814
+ layout={layoutOverrides}${clientData}
1815
+ logo={data.config.logo}
1816
+ mcp={data.config.mcp}
1817
+ favicon={data.config.favicon}
1818
+ appleIcon={data.config.appleIcon}
1819
+ banner={data.config.banner}
1820
+ analytics={data.config.analytics}
1821
+ imageZoom={data.config.imageZoom}
1822
+ codeWrap={data.config.codeWrap}
1823
+ navigation={data.navigation}
1824
+ locale={htmlLang}
1825
+ dir={dir}
1826
+ ui={data.ui}
1827
+ page={{
1828
+ title: pageTitle,
1829
+ description: changelogDescription,
1830
+ route: "/changelog",
1831
+ }}
1832
+ headings={headings}
1833
+ toc={data.config.toc}
1834
+ contentLayout="bare"
1835
+ themeMode={data.config.theme.mode}
1836
+ fontCssVars={data.fontCssVars}
1837
+ searchEnabled={data.config.search.enabled}
1838
+ indexable={true}
1839
+ ogImage={ogImage}
1840
+ ogGenerated={Boolean(ogImage)}
1841
+ x={data.config.x}
1842
+ canonical={canonical}
1843
+ exportPdf={${options.exportPdf}}
1844
+ exportEpub={${options.exportEpub}}
1845
+ openInChat={data.config.openInChat}
1846
+ feeds={data.feeds}
1847
+ discovery={data.config.discovery}
1848
+ siteUrl={data.config.site}
1849
+ noindex={false}
1850
+ structuredDataEnabled={data.config.structuredData}
1851
+ >
1852
+ <h1>{changelogTitle}</h1>
1853
+ {
1854
+ items.length === 0 ? (
1855
+ <p>No changelog entries yet.</p>
1856
+ ) : paginate ? (
1857
+ <blume-changelog
1858
+ class="not-prose mt-8 block"
1859
+ data-i18n-more={data.ui.changelog?.showReleases}
1860
+ >
1861
+ {majorGroups[0].items.map(({ Content, href, id, label, date, tags }) => (
1862
+ <Update description={date} href={href} id={id} label={label} tags={tags}>
1863
+ <Content />
1864
+ </Update>
1865
+ ))}
1866
+ {majorGroups.slice(1).map((group) => (
1867
+ <section
1868
+ aria-label={group.label + " releases"}
1869
+ data-changelog-label={group.label}
1870
+ data-changelog-major={group.major}
1871
+ >
1872
+ {group.items.map(({ Content, href, id, label, date, tags }) => (
1873
+ <Update description={date} href={href} id={id} label={label} tags={tags}>
1874
+ <Content />
1875
+ </Update>
1876
+ ))}
1877
+ </section>
1878
+ ))}
1879
+ <div class="mt-10 flex justify-center">
1880
+ <button
1881
+ class="inline-flex items-center gap-2 rounded-full border border-border bg-background px-4 py-2 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground"
1882
+ data-changelog-more
1883
+ hidden
1884
+ type="button"
1885
+ >
1886
+ Show older releases
1887
+ </button>
1888
+ </div>
1889
+ </blume-changelog>
1890
+ ) : (
1891
+ <div class="not-prose mt-8">
1892
+ {items.map(({ Content, href, id, label, date, tags }) => (
1893
+ <Update description={date} href={href} id={id} label={label} tags={tags}>
1894
+ <Content />
1895
+ </Update>
1896
+ ))}
1897
+ </div>
1898
+ )
1899
+ }
1900
+ <script>
1901
+ import "blume/components/content/changelog-element.ts";
1902
+ </script>
1903
+ </LayoutComponent>
1904
+ `;
1905
+ };
1906
+ var notFoundPageTemplate = () => `---
1907
+ // Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
1908
+ import Icon from "blume/components/Icon.astro";
1909
+ import PageLayout from "blume/components/layout/PageLayout.astro";
1910
+ import { withBase } from "blume/components/islands/base-path.ts";
1911
+ import data from "blume:data";
1912
+
1913
+ export const prerender = true;
1914
+
1915
+ const nf = data.ui.notFound;
1916
+
1917
+ // The 404 page is an unlocalized route, so its chrome renders in the default
1918
+ // locale's dictionary and direction (\`data.ui\` is the default locale's resolved
1919
+ // dictionary), mirroring the catch-all's locale wiring.
1920
+ const i18n = data.config.i18n;
1921
+ const localeMeta = i18n
1922
+ ? i18n.locales.find((l) => l.code === i18n.defaultLocale)
1923
+ : null;
1924
+ const dir = localeMeta?.dir ?? "ltr";
1925
+ const htmlLang = i18n ? i18n.defaultLocale : "en";
1926
+
1927
+ // Recovery links, so a reader — or an agent that followed a stale URL — can
1928
+ // get back on track without guessing: every top-level section, then the
1929
+ // machine-readable indexes the build emits (the sitemap only exists with a
1930
+ // \`deployment.site\`; llms.txt only when \`ai.llmsTxt\` is on). Tabs link to
1931
+ // their resolved target when the section has no index page of its own.
1932
+ const suggestions = [
1933
+ ...data.navigation.tabs.map((tab) => ({
1934
+ href: withBase(tab.href ?? tab.path),
1935
+ label: tab.label,
1936
+ })),
1937
+ ...(data.config.discovery.sitemap
1938
+ ? [{ href: withBase("/sitemap.xml"), label: nf.sitemap }]
1939
+ : []),
1940
+ ...(data.config.discovery.llmsTxt
1941
+ ? [{ href: withBase("/llms.txt"), label: nf.llms }]
1942
+ : []),
1943
+ ];
1944
+ ---
1945
+
1946
+ <PageLayout
1947
+ site={{ title: data.config.title, description: data.config.description }}
1948
+ logo={data.config.logo}
1949
+ favicon={data.config.favicon}
1950
+ appleIcon={data.config.appleIcon}
1951
+ banner={data.config.banner}
1952
+ analytics={data.config.analytics}
1953
+ navigation={data.navigation}
1954
+ page={{ title: nf.title, route: "/404" }}
1955
+ themeMode={data.config.theme.mode}
1956
+ fontCssVars={data.fontCssVars}
1957
+ searchEnabled={data.config.search.enabled}
1958
+ locale={htmlLang}
1959
+ dir={dir}
1960
+ ui={data.ui}
1961
+ noindex={true}
1962
+ >
1963
+ <div
1964
+ class="mx-auto grid w-full max-w-5xl gap-12 px-6 py-20 sm:py-28 md:grid-cols-[3fr_2fr] md:gap-16 lg:gap-24 lg:py-36"
1965
+ >
1966
+ <div class="flex flex-col items-start">
1967
+ <p
1968
+ class="font-mono text-xs font-medium tracking-widest text-muted-foreground"
1969
+ >
1970
+ 404
1971
+ </p>
1972
+ <h1
1973
+ class="mt-4 text-balance text-4xl font-semibold tracking-tight text-foreground sm:text-5xl"
1974
+ >
1975
+ {nf.title}
1976
+ </h1>
1977
+ <p class="mt-4 max-w-md text-pretty text-lg text-muted-foreground">
1978
+ {nf.description}
1979
+ </p>
1980
+ <a
1981
+ class="mt-8 inline-flex items-center gap-1.5 rounded-full bg-accent py-2 pe-4 ps-3.5 text-sm font-medium text-accent-foreground transition-opacity hover:opacity-90"
1982
+ href={withBase("/")}
1983
+ >
1984
+ <Icon class="rtl:-scale-x-100" name="arrow-left" size={14} />
1985
+ {nf.home}
1986
+ </a>
1987
+ </div>
1988
+ {
1989
+ suggestions.length > 0 && (
1990
+ <nav aria-label={nf.suggestions} class="md:border-s md:border-border md:ps-12 lg:ps-16">
1991
+ <h2 class="text-xs font-medium uppercase tracking-widest text-muted-foreground">
1992
+ {nf.suggestions}
1993
+ </h2>
1994
+ <ul class="mt-4 divide-y divide-border border-y border-border">
1995
+ {suggestions.map((link) => (
1996
+ <li>
1997
+ <a
1998
+ class="group flex items-center justify-between gap-4 py-3 text-sm font-medium text-foreground transition-colors hover:text-accent"
1999
+ href={link.href}
2000
+ >
2001
+ <span>{link.label}</span>
2002
+ <Icon
2003
+ class="shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5 rtl:-scale-x-100 rtl:group-hover:-translate-x-0.5"
2004
+ name="arrow-right"
2005
+ size={14}
2006
+ />
2007
+ </a>
2008
+ </li>
2009
+ ))}
2010
+ </ul>
2011
+ </nav>
2012
+ )
2013
+ }
2014
+ </div>
2015
+ </PageLayout>
2016
+ `;
2017
+ var notFoundMarkdownTemplate = () => `// Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
2018
+ import { withBase } from "blume/components/islands/base-path.ts";
2019
+ import { absoluteUrl } from "blume/core/site-url.ts";
2020
+ import data from "blume:data";
2021
+
2022
+ export const prerender = true;
2023
+
2024
+ const nf = data.ui.notFound;
2025
+
2026
+ // Absolute for internal routes when the site is known; an external tab href
2027
+ // passes through untouched.
2028
+ const href = (path: string): string => {
2029
+ const based = withBase(path);
2030
+ return data.config.site && based.startsWith("/") && !based.startsWith("//")
2031
+ ? absoluteUrl(data.config.site, based)
2032
+ : based;
2033
+ };
2034
+
2035
+ // The recovery set of 404.astro: home, every top-level section (a tab links to
2036
+ // its resolved target), then the machine-readable indexes that exist.
2037
+ const links = [
2038
+ { href: href("/"), label: nf.home },
2039
+ ...data.navigation.tabs.map((tab) => ({
2040
+ href: href(tab.href ?? tab.path),
2041
+ label: tab.label,
2042
+ })),
2043
+ ...(data.config.discovery.sitemap
2044
+ ? [{ href: href("/sitemap.xml"), label: nf.sitemap }]
2045
+ : []),
2046
+ ...(data.config.discovery.llmsTxt
2047
+ ? [{ href: href("/llms.txt"), label: nf.llms }]
2048
+ : []),
2049
+ ...(data.config.discovery.api
2050
+ ? [{ href: href("/openapi.json"), label: nf.api }]
2051
+ : []),
2052
+ ];
2053
+
2054
+ const body = [
2055
+ "# " + nf.title,
2056
+ "",
2057
+ nf.description,
2058
+ "",
2059
+ "## " + nf.suggestions,
2060
+ "",
2061
+ ...links.map((link) => "- [" + link.label + "](" + link.href + ")"),
2062
+ "",
2063
+ ].join("\\n");
2064
+
2065
+ export function GET() {
2066
+ return new Response(body, {
2067
+ headers: {
2068
+ "Content-Type": "text/markdown; charset=utf-8",
2069
+ // ~4 characters per token; keep in sync with markdownTokenCount.
2070
+ "x-markdown-tokens": String(Math.ceil(body.length / 4)),
2071
+ },
2072
+ });
2073
+ }
2074
+ `;
2075
+ var notFoundJsonTemplate = () => `// Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
2076
+ import { problem } from "blume/ai/api/problem.ts";
2077
+ import { withBase } from "blume/components/islands/base-path.ts";
2078
+ import { absoluteUrl } from "blume/core/site-url.ts";
2079
+ import data from "blume:data";
2080
+
2081
+ export const prerender = true;
2082
+
2083
+ const nf = data.ui.notFound;
2084
+
2085
+ // Absolute for internal routes when the site is known; an external tab href
2086
+ // passes through untouched.
2087
+ const href = (path: string): string => {
2088
+ const based = withBase(path);
2089
+ return data.config.site && based.startsWith("/") && !based.startsWith("//")
2090
+ ? absoluteUrl(data.config.site, based)
2091
+ : based;
2092
+ };
2093
+
2094
+ // The recovery set of 404.astro: home, every top-level section (a tab links to
2095
+ // its resolved target), then the machine-readable indexes that exist.
2096
+ const links = [
2097
+ { href: href("/"), label: nf.home },
2098
+ ...data.navigation.tabs.map((tab) => ({
2099
+ href: href(tab.href ?? tab.path),
2100
+ label: tab.label,
2101
+ })),
2102
+ ...(data.config.discovery.sitemap
2103
+ ? [{ href: href("/sitemap.xml"), label: nf.sitemap }]
2104
+ : []),
2105
+ ...(data.config.discovery.llmsTxt
2106
+ ? [{ href: href("/llms.txt"), label: nf.llms }]
2107
+ : []),
2108
+ ...(data.config.discovery.api
2109
+ ? [{ href: href("/openapi.json"), label: nf.api }]
2110
+ : []),
2111
+ ];
2112
+
2113
+ const body = problem({
2114
+ code: "PAGE_NOT_FOUND",
2115
+ detail: nf.description,
2116
+ links,
2117
+ resolution: nf.suggestions + ": " + links.map((link) => link.href).join(", "),
2118
+ status: 404,
2119
+ title: nf.title,
2120
+ });
2121
+
2122
+ export function GET() {
2123
+ return new Response(JSON.stringify(body, null, 2) + "\\n", {
2124
+ headers: { "Content-Type": "application/problem+json; charset=utf-8" },
2125
+ });
2126
+ }
2127
+ `;
2128
+ var apiPagesIndexTemplate = () => `// Generated by Blume. Do not edit.
2129
+ import { pagesIndexResponse } from "blume/ai/api/handlers.ts";
2130
+ import data from "blume:mcp-data";
2131
+
2132
+ export const prerender = true;
2133
+
2134
+ export function GET() {
2135
+ return pagesIndexResponse(data);
2136
+ }
2137
+ `;
2138
+ var apiPageTemplate = () => `// Generated by Blume. Do not edit.
2139
+ import { pageParams, pageResponse } from "blume/ai/api/handlers.ts";
2140
+ import data from "blume:mcp-data";
2141
+
2142
+ export const prerender = true;
2143
+
2144
+ export function getStaticPaths() {
2145
+ return pageParams(data);
2146
+ }
2147
+
2148
+ export function GET({ props }: { props: { route: string } }) {
2149
+ return pageResponse(data, props.route);
2150
+ }
2151
+ `;
2152
+ var apiNavigationTemplate = () => `// Generated by Blume. Do not edit.
2153
+ import { navigationResponse } from "blume/ai/api/handlers.ts";
2154
+ import data from "blume:mcp-data";
2155
+
2156
+ export const prerender = true;
2157
+
2158
+ export function GET() {
2159
+ return navigationResponse(data);
2160
+ }
2161
+ `;
2162
+ var apiSearchTemplate = () => `// Generated by Blume. Do not edit.
2163
+ import type { APIRoute } from "astro";
2164
+ import { createSearchHandler } from "blume/ai/api/handlers.ts";
2165
+ import data from "blume:mcp-data";
2166
+
2167
+ export const prerender = false;
2168
+
2169
+ const handler = createSearchHandler(data);
2170
+
2171
+ export const GET: APIRoute = ({ request }) => handler(request);
2172
+ `;
2173
+ var apiNotFoundTemplate = (context) => `// Generated by Blume. Do not edit.
2174
+ import type { APIRoute } from "astro";
2175
+ import { apiNotFoundResponse } from "blume/ai/api/handlers.ts";
2176
+
2177
+ export const prerender = false;
2178
+
2179
+ const context = ${JSON.stringify(context)};
2180
+
2181
+ export const ALL: APIRoute = ({ request }) => apiNotFoundResponse(request, context);
2182
+ `;
2183
+ var islandDirective = (spec) => spec.client === "only" ? `client:only="${spec.framework}"` : `client:${spec.client}`;
2184
+ var wrapperPropsType = (name) => `type Props = typeof ${name} extends (
2185
+ props: infer P extends object,
2186
+ ...rest: never[]
2187
+ ) => unknown
2188
+ ? P
2189
+ : Record<string, unknown>;`;
2190
+ var islandWrapperTemplate = (spec) => `---
2191
+ // Generated by Blume. Do not edit.
2192
+ import Island from ${JSON.stringify(spec.file)};
2193
+ ${wrapperPropsType("Island")}
2194
+ ---
2195
+ <Island ${islandDirective(spec)} {...Astro.props}><slot /></Island>
2196
+ `;
2197
+ var islandMapTemplate = (specs) => {
2198
+ if (specs.length === 0) {
2199
+ return `// Generated by Blume. Do not edit.
2200
+ export const islandComponents = {};
2201
+ `;
2202
+ }
2203
+ const imports = specs.map((spec, index) => `import I${index} from "./islands/${spec.name}.astro";`).join(`
2204
+ `);
2205
+ const entries = specs.map((spec, index) => ` ${spec.name}: I${index},`).join(`
2206
+ `);
2207
+ return `// Generated by Blume. Do not edit.
2208
+ ${imports}
2209
+ export const islandComponents = {
2210
+ ${entries}
2211
+ };
2212
+ `;
2213
+ };
2214
+ var exampleDirective = (spec) => {
2215
+ if (spec.framework === "astro" || !spec.client) {
2216
+ return "";
2217
+ }
2218
+ return spec.client === "only" ? `client:only="${spec.framework}" ` : `client:${spec.client} `;
2219
+ };
2220
+ var exampleSlug = (path) => path.replaceAll(/[^a-zA-Z0-9]/gu, (char) => `_${(char.codePointAt(0) ?? 0).toString(16)}_`);
2221
+ var exampleWrapperTemplate = (spec) => `---
2222
+ // Generated by Blume. Do not edit.
2223
+ import Example from ${JSON.stringify(spec.file)};
2224
+ ${wrapperPropsType("Example")}
2225
+ ---
2226
+ <Example ${exampleDirective(spec)}{...Astro.props}><slot /></Example>
2227
+ `;
2228
+ var examplesRouteBase = (basePath) => `${basePath}/blume-examples`;
2229
+ var exampleMapTemplate = (specs, basePath) => {
2230
+ const base = `export const examplesBase = ${JSON.stringify(examplesRouteBase(basePath))};`;
2231
+ if (specs.length === 0) {
2232
+ return `// Generated by Blume. Do not edit.
2233
+ ${base}
2234
+ export const examples = {};
2235
+ `;
2236
+ }
2237
+ const imports = specs.map((spec, index) => `import E${index} from "./examples/${exampleSlug(spec.path)}.astro";`).join(`
2238
+ `);
2239
+ const entries = specs.map((spec, index) => ` ${JSON.stringify(spec.path)}: { Component: E${index}, code: ${JSON.stringify(spec.source)}, lang: ${JSON.stringify(spec.lang)} },`).join(`
2240
+ `);
2241
+ return `// Generated by Blume. Do not edit.
2242
+ ${imports}
2243
+ ${base}
2244
+ export const examples = {
2245
+ ${entries}
2246
+ };
2247
+ `;
2248
+ };
2249
+ var examplesPageTemplate = () => `---
2250
+ // Generated by Blume. Do not edit.
2251
+ import { examples } from "blume:examples";
2252
+ import "blume:examples-theme";
2253
+
2254
+ // Prerendered even in server output, like docs content.
2255
+ export const prerender = true;
2256
+
2257
+ export const getStaticPaths = () =>
2258
+ Object.keys(examples).map((path) => ({ params: { path } }));
2259
+
2260
+ const { path } = Astro.params;
2261
+ const entry = path ? examples[path] : undefined;
2262
+ if (!entry) {
2263
+ return new Response(null, { status: 404 });
2264
+ }
2265
+ const Example = entry.Component;
2266
+ ---
2267
+
2268
+ <html lang="en">
2269
+ <head>
2270
+ <meta charset="utf-8" />
2271
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
2272
+ <meta name="robots" content="noindex" />
2273
+ <title>{path}</title>
2274
+ <script is:inline>
2275
+ (() => {
2276
+ const root = document.documentElement;
2277
+ const apply = (theme) => {
2278
+ root.dataset.theme = theme;
2279
+ root.classList.toggle("dark", theme === "dark");
2280
+ };
2281
+ const stored = () =>
2282
+ localStorage.getItem("blume-theme") ??
2283
+ (matchMedia("(prefers-color-scheme: dark)").matches
2284
+ ? "dark"
2285
+ : "light");
2286
+ try {
2287
+ const host = window.parent.document.documentElement;
2288
+ apply(host.dataset.theme ?? stored());
2289
+ new MutationObserver(() => {
2290
+ apply(host.dataset.theme ?? stored());
2291
+ }).observe(host, { attributeFilter: ["data-theme"] });
2292
+ } catch {
2293
+ apply(stored());
2294
+ }
2295
+ })();
2296
+ </script>
2297
+ </head>
2298
+ <!-- Flex + margin:auto centers the example and, unlike place-items, keeps
2299
+ the top edge reachable when the example outgrows the frame. -->
2300
+ <body style="display:flex;min-height:100svh;padding:1.5rem">
2301
+ <div data-blume-example style="margin:auto"><Example /></div>
2302
+ <script is:inline>
2303
+ (() => {
2304
+ // Report the example's rendered height so the embedding docs page can
2305
+ // size the preview pane to the content. The wrapper is observed rather
2306
+ // than the body: the body stretches to the frame's own height, so it
2307
+ // would only echo the pane back. Direct opens have no distinct parent
2308
+ // and skip out; the frame is same-origin with the docs page (see the
2309
+ // theme sync above), so the origin is pinned on both ends.
2310
+ if (window.parent === window) {
2311
+ return;
2312
+ }
2313
+ const wrapper = document.querySelector("[data-blume-example]");
2314
+ if (!wrapper) {
2315
+ return;
2316
+ }
2317
+ // The body's padding frames the example; fold it into the report so
2318
+ // the parent can apply the number as-is. Read from the live value —
2319
+ // the user's examples.css is injected after Blume's defaults precisely
2320
+ // so their tokens win, so a root font-size override must be honored
2321
+ // rather than assuming 1.5rem is 48px.
2322
+ const bodyStyle = getComputedStyle(document.body);
2323
+ const paddingPx =
2324
+ parseFloat(bodyStyle.paddingTop) + parseFloat(bodyStyle.paddingBottom);
2325
+ const report = () => {
2326
+ window.parent.postMessage(
2327
+ {
2328
+ height:
2329
+ Math.ceil(wrapper.getBoundingClientRect().height) + paddingPx,
2330
+ type: "blume:example-height",
2331
+ },
2332
+ window.location.origin
2333
+ );
2334
+ };
2335
+ new ResizeObserver(report).observe(wrapper);
2336
+ // The parent asks for a fresh report when its listener comes up, in
2337
+ // case the first one above was posted before anyone was listening.
2338
+ window.addEventListener("message", (event) => {
2339
+ if (
2340
+ event.source === window.parent &&
2341
+ event.origin === window.location.origin &&
2342
+ event.data?.type === "blume:example-height-request"
2343
+ ) {
2344
+ report();
2345
+ }
2346
+ });
2347
+ })();
2348
+ </script>
2349
+ </body>
2350
+ </html>
2351
+ `;
2352
+ var runtimePackageTemplate = (dependencies = []) => `${JSON.stringify({
2353
+ dependencies: Object.fromEntries([...dependencies].toSorted().map((name) => [name, "*"])),
2354
+ name: "blume-runtime",
2355
+ private: true,
2356
+ type: "module",
2357
+ version: "0.0.0"
2358
+ }, null, 2)}
2359
+ `;
2360
+ var runtimeTsconfigTemplate = () => `${JSON.stringify({
2361
+ exclude: ["dist"],
2362
+ extends: "astro/tsconfigs/strict",
2363
+ include: [".astro/types.d.ts", "**/*"]
2364
+ }, null, 2)}
2365
+ `;
2366
+
2367
+ export { ADAPTER_IGNORE_DIRS, servesClientSubdir, deployStaticDir, surfaceAdapterOutput, resolveAskBackend, askBackendRuntimeDep, buildHomeLinkHeader, platformRedirects, publishRuntimeModules, runtimeDependencies, astroConfigTemplate, stagedContentDir, contentConfigTemplate, askEndpointTemplate, askComponentTemplate, searchEndpointTemplate, searchClientTemplate, mixedbreadSearchEndpointTemplate, rawMarkdownEndpointTemplate, contentAssetsEndpointTemplate, mcpPageFile, mcpEndpointTemplate, playgroundProxyTemplate, staticJsonEndpointTemplate, rssEndpointTemplate, ogEndpointTemplate, scalarReferenceTemplate, catchAllPageTemplate, changelogIndexTemplate, notFoundPageTemplate, notFoundMarkdownTemplate, notFoundJsonTemplate, apiPagesIndexTemplate, apiPageTemplate, apiNavigationTemplate, apiSearchTemplate, apiNotFoundTemplate, islandWrapperTemplate, islandMapTemplate, exampleSlug, exampleWrapperTemplate, examplesRouteBase, exampleMapTemplate, examplesPageTemplate, runtimePackageTemplate, runtimeTsconfigTemplate };
2368
+
2369
+ //# debugId=1666B85D4E0F438E64756E2164756E21
2370
+ //# sourceMappingURL=chunk-aerwpe14.js.map