@dogsbay/format-astro 0.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Plugin-runtime codegen — the format-astro side of the plugin API.
3
+ *
4
+ * Each Dogsbay site build emits a fresh set of plugin-runtime
5
+ * source files in the generated Astro project so plugins'
6
+ * client-side code, virtual config modules, and stylesheets
7
+ * participate in the user's Astro / Vite / Tailwind build.
8
+ *
9
+ * The emitters are intentionally idempotent: running a build twice
10
+ * with the same plugin set produces the same output. When a plugin
11
+ * is removed from config, the next build leaves orphan files
12
+ * untouched (cheap; format-astro never deletes user-modifiable
13
+ * files); the orphans are inert because no `import` references
14
+ * them. A `pnpm clean` clears them.
15
+ *
16
+ * Files written:
17
+ *
18
+ * src/lib/plugin-runtime.ts
19
+ * The single entry point DocsLayout's <script> imports. Each
20
+ * plugin's clientModules paths get an `import "..."` line in
21
+ * declaration order.
22
+ *
23
+ * src/lib/plugin-config/<id>.ts
24
+ * One per plugin that called `defineClientConfig`. Exports the
25
+ * value as `default` so client modules can do
26
+ * `import config from "virtual:dogsbay-plugin-config/<id>"`.
27
+ *
28
+ * src/styles/plugins/<id>.css
29
+ * Copy of each plugin's `styles()` files. Imported into
30
+ * `src/styles/global.css` via an injected block delimited by
31
+ * marker comments — re-runs replace just that block.
32
+ *
33
+ * astro.config.plugins.mjs
34
+ * Module that exports a Vite alias map keyed by virtual config
35
+ * name. astro.config.mjs imports + spreads it; the alias map
36
+ * is tiny (one entry per plugin with defineClientConfig).
37
+ *
38
+ * See plans/plugin-api.md.
39
+ */
40
+ import { writeFileSync, readFileSync, existsSync, mkdirSync, copyFileSync, rmSync } from "node:fs";
41
+ import { dirname, join, basename, extname } from "node:path";
42
+ /**
43
+ * Slots the runtime emits stack files for. Each entry is always
44
+ * emitted (even when no plugin contributes) so per-page emission
45
+ * can unconditionally import + use the stack. Adding a new slot
46
+ * requires both an entry here and a usage site in format-astro's
47
+ * page emitter.
48
+ */
49
+ const KNOWN_SLOTS = ["MarkdownContent"];
50
+ const PLUGIN_RUNTIME_REL = "src/lib/plugin-runtime.ts";
51
+ const PLUGIN_CONFIG_DIR_REL = "src/lib/plugin-config";
52
+ const PLUGIN_STYLES_DIR_REL = "src/styles/plugins";
53
+ const PLUGIN_WRAPPERS_DIR_REL = "src/components/wrappers";
54
+ const PLUGIN_VITE_REL = "astro.config.plugins.mjs";
55
+ const GLOBAL_CSS_REL = "src/styles/global.css";
56
+ const GLOBAL_CSS_BEGIN = "/* dogsbay:plugins:begin */";
57
+ const GLOBAL_CSS_END = "/* dogsbay:plugins:end */";
58
+ /**
59
+ * Emit the plugin-runtime artefacts. Safe to call when there are
60
+ * no plugins — every emitted file is shaped to compile and behave
61
+ * as a no-op when no plugin contributes that surface.
62
+ */
63
+ export function emitPluginRuntime(options) {
64
+ emitClientModulesEntry(options);
65
+ emitClientConfigModules(options);
66
+ emitPluginStyles(options);
67
+ emitPluginViteAliases(options);
68
+ emitWrapperStacks(options);
69
+ }
70
+ /**
71
+ * Write `src/lib/plugin-runtime.ts` — the single entry point
72
+ * DocsLayout pulls in. One import per client-module path, in
73
+ * plugin declaration order.
74
+ */
75
+ function emitClientModulesEntry(options) {
76
+ const lines = [
77
+ "// Auto-generated by dogsbay site build. Do not edit by hand.",
78
+ "// One import per plugin client module, in declaration order.",
79
+ "// See plans/plugin-api.md → \"Build pipeline integration\".",
80
+ "",
81
+ ];
82
+ for (const { pluginId, paths } of options.clientModules) {
83
+ lines.push(`// ${pluginId}`);
84
+ for (const p of paths) {
85
+ // Convert absolute paths to a Vite-friendly URL form. Astro
86
+ // can resolve absolute fs paths via Vite's `/@fs/` prefix in
87
+ // dev, and at build time Vite/rollup follow them directly.
88
+ lines.push(`import ${JSON.stringify(p)};`);
89
+ }
90
+ }
91
+ if (options.clientModules.length === 0) {
92
+ lines.push("// No plugin client modules registered.");
93
+ }
94
+ lines.push("");
95
+ writeFile(join(options.outputDir, PLUGIN_RUNTIME_REL), lines.join("\n"));
96
+ }
97
+ /**
98
+ * Write one TypeScript file per plugin that called
99
+ * `defineClientConfig`. Each file `export default` the JSON-
100
+ * serialized value so client modules import a typed default.
101
+ */
102
+ function emitClientConfigModules(options) {
103
+ const dir = join(options.outputDir, PLUGIN_CONFIG_DIR_REL);
104
+ if (options.clientConfigs.length === 0) {
105
+ // Nothing to emit; leave the dir alone if it exists from prior
106
+ // builds (idempotency rule above).
107
+ return;
108
+ }
109
+ mkdirSync(dir, { recursive: true });
110
+ for (const { pluginId, value } of options.clientConfigs) {
111
+ const fileName = `${slugifyPluginId(pluginId)}.ts`;
112
+ const safeJson = JSON.stringify(value, null, 2);
113
+ const contents = [
114
+ `// Auto-generated config for plugin "${pluginId}".`,
115
+ `// Imported by client modules via`,
116
+ `// virtual:dogsbay-plugin-config/${pluginId}`,
117
+ "",
118
+ `export default ${safeJson};`,
119
+ "",
120
+ ].join("\n");
121
+ writeFile(join(dir, fileName), contents);
122
+ }
123
+ }
124
+ /**
125
+ * Copy plugin styles into `src/styles/plugins/<plugin-id>__<file>.css`
126
+ * and inject `@import` lines into `src/styles/global.css` between
127
+ * the marker comments. The marker block is replaced (not appended)
128
+ * so re-runs are idempotent.
129
+ */
130
+ function emitPluginStyles(options) {
131
+ const stylesDir = join(options.outputDir, PLUGIN_STYLES_DIR_REL);
132
+ const imports = [];
133
+ if (options.styles.length > 0) {
134
+ mkdirSync(stylesDir, { recursive: true });
135
+ for (const { pluginId, paths } of options.styles) {
136
+ for (const p of paths) {
137
+ const ext = extname(p) || ".css";
138
+ const baseName = basename(p, ext);
139
+ const dest = `${slugifyPluginId(pluginId)}__${baseName}${ext}`;
140
+ copyFileSync(p, join(stylesDir, dest));
141
+ imports.push(`@import "./plugins/${dest}";`);
142
+ }
143
+ }
144
+ }
145
+ // Patch global.css. If the file doesn't exist (e.g. a partial
146
+ // scaffold), leave it alone — the scaffold emitter owns global.css.
147
+ const cssPath = join(options.outputDir, GLOBAL_CSS_REL);
148
+ if (!existsSync(cssPath))
149
+ return;
150
+ const current = readFileSync(cssPath, "utf-8");
151
+ const block = imports.length > 0
152
+ ? `${GLOBAL_CSS_BEGIN}\n${imports.join("\n")}\n${GLOBAL_CSS_END}`
153
+ : `${GLOBAL_CSS_BEGIN}\n${GLOBAL_CSS_END}`;
154
+ let next;
155
+ const beginIdx = current.indexOf(GLOBAL_CSS_BEGIN);
156
+ const endIdx = current.indexOf(GLOBAL_CSS_END);
157
+ if (beginIdx >= 0 && endIdx > beginIdx) {
158
+ next =
159
+ current.slice(0, beginIdx) +
160
+ block +
161
+ current.slice(endIdx + GLOBAL_CSS_END.length);
162
+ }
163
+ else {
164
+ // Append at end; ensure trailing newline.
165
+ const trimmed = current.endsWith("\n") ? current : `${current}\n`;
166
+ next = `${trimmed}${block}\n`;
167
+ }
168
+ writeFileSync(cssPath, next);
169
+ }
170
+ /**
171
+ * Write `astro.config.plugins.mjs` — a tiny module exporting:
172
+ * - `pluginAliases`: Vite alias map for `virtual:dogsbay-plugin-config/<id>`
173
+ * - `pluginFsAllow`: array of directory paths to add to
174
+ * `vite.server.fs.allow`, so plugins shipped from absolute
175
+ * paths outside the project root (workspace deps, monorepo
176
+ * layouts) can be served by the Vite dev server.
177
+ *
178
+ * The user's `astro.config.mjs` imports both and spreads them
179
+ * into the appropriate Vite config slots.
180
+ */
181
+ function emitPluginViteAliases(options) {
182
+ const aliasEntries = [];
183
+ for (const { pluginId } of options.clientConfigs) {
184
+ const target = `./${PLUGIN_CONFIG_DIR_REL}/${slugifyPluginId(pluginId)}.ts`;
185
+ aliasEntries.push(` ${JSON.stringify(`virtual:dogsbay-plugin-config/${pluginId}`)}: new URL(${JSON.stringify(target)}, import.meta.url).pathname,`);
186
+ }
187
+ // Collect parent dirs of every clientModule + style file. Vite
188
+ // refuses to serve files outside the project root unless the
189
+ // path is on `server.fs.allow`. We add the immediate parent;
190
+ // Vite's allow check is recursive.
191
+ const fsAllowSet = new Set();
192
+ for (const { paths } of options.clientModules) {
193
+ for (const p of paths)
194
+ fsAllowSet.add(dirname(p));
195
+ }
196
+ for (const { paths } of options.styles) {
197
+ for (const p of paths)
198
+ fsAllowSet.add(dirname(p));
199
+ }
200
+ const contents = [
201
+ "// Auto-generated by dogsbay site build. Do not edit by hand.",
202
+ "// astro.config.mjs imports + spreads pluginAliases into",
203
+ "// vite.resolve.alias and pluginFsAllow into vite.server.fs.allow.",
204
+ "",
205
+ "export const pluginAliases = {",
206
+ ...aliasEntries,
207
+ "};",
208
+ "",
209
+ "export const pluginFsAllow = [",
210
+ ...Array.from(fsAllowSet).map((p) => ` ${JSON.stringify(p)},`),
211
+ "];",
212
+ "",
213
+ ].join("\n");
214
+ writeFile(join(options.outputDir, PLUGIN_VITE_REL), contents);
215
+ }
216
+ /**
217
+ * For each known slot, emit a `<Slot>Stack.astro` component that
218
+ * imports every contributing plugin's wrapper and recursively
219
+ * nests them. Plugin order maps to outer→inner — the first
220
+ * plugin's wrapper is the outermost.
221
+ *
222
+ * When no plugin contributes to a slot, the stack file is a
223
+ * passthrough (`<slot />`). Always emitting the file means
224
+ * per-page emission can unconditionally import and use the stack
225
+ * regardless of which plugins are loaded.
226
+ */
227
+ function emitWrapperStacks(options) {
228
+ const wrappersDir = join(options.outputDir, PLUGIN_WRAPPERS_DIR_REL);
229
+ mkdirSync(wrappersDir, { recursive: true });
230
+ const groups = groupWrappersBySlot(options.componentWrappers ?? []);
231
+ for (const slot of KNOWN_SLOTS) {
232
+ const entries = groups.get(slot) ?? [];
233
+ const stackPath = join(wrappersDir, `${slot}Stack.astro`);
234
+ writeFile(stackPath, renderStack(slot, entries));
235
+ }
236
+ }
237
+ function groupWrappersBySlot(wrappers) {
238
+ const out = new Map();
239
+ for (const w of wrappers) {
240
+ const list = out.get(w.slot) ?? [];
241
+ list.push(w);
242
+ out.set(w.slot, list);
243
+ }
244
+ return out;
245
+ }
246
+ /**
247
+ * Render the source of `<Slot>Stack.astro`. Recursive nesting:
248
+ * first plugin → outermost,
249
+ * last plugin → innermost,
250
+ * final inner content → `<slot />`.
251
+ */
252
+ function renderStack(slotName, entries) {
253
+ if (entries.length === 0) {
254
+ return [
255
+ `---`,
256
+ `// Auto-generated by dogsbay site build. No plugin contributed`,
257
+ `// a wrapper for the "${slotName}" slot — passthrough.`,
258
+ `---`,
259
+ ``,
260
+ `<slot />`,
261
+ ``,
262
+ ].join("\n");
263
+ }
264
+ // Build import statements, one per entry. Identifiers must be
265
+ // unique across the file — qualify with plugin id + index.
266
+ const importLines = [];
267
+ const componentNames = [];
268
+ entries.forEach((entry, idx) => {
269
+ const ident = `Wrap_${idx}_${slugifyIdent(entry.pluginId)}`;
270
+ importLines.push(`import ${ident} from ${JSON.stringify(entry.path)};`);
271
+ componentNames.push(ident);
272
+ });
273
+ // Pretty-print the nested body for readability — one tag per
274
+ // line with two-space indent per level.
275
+ const formatted = formatNested(componentNames);
276
+ return [
277
+ `---`,
278
+ `// Auto-generated by dogsbay site build. Wrappers contributed by:`,
279
+ ...entries.map((e, i) => `// ${i + 1}. ${e.pluginId} → ${e.path}`),
280
+ `// Order: outer → inner = first → last plugin.`,
281
+ importLines.join("\n"),
282
+ `---`,
283
+ ``,
284
+ formatted,
285
+ ``,
286
+ ].join("\n");
287
+ }
288
+ /**
289
+ * Render an indented tree of nested wrapper components for
290
+ * readability. Output:
291
+ * <A>
292
+ * <B>
293
+ * <slot />
294
+ * </B>
295
+ * </A>
296
+ */
297
+ function formatNested(names) {
298
+ const lines = [];
299
+ names.forEach((name, idx) => {
300
+ const indent = " ".repeat(idx);
301
+ lines.push(`${indent}<${name}>`);
302
+ });
303
+ lines.push(`${" ".repeat(names.length)}<slot />`);
304
+ for (let i = names.length - 1; i >= 0; i--) {
305
+ const indent = " ".repeat(i);
306
+ lines.push(`${indent}</${names[i]}>`);
307
+ }
308
+ return lines.join("\n");
309
+ }
310
+ /**
311
+ * Convert a plugin id into a JavaScript-safe identifier suffix.
312
+ * Used as a uniqueness qualifier on the imported component name.
313
+ */
314
+ function slugifyIdent(id) {
315
+ return id.replace(/[^A-Za-z0-9_]/g, "_");
316
+ }
317
+ /**
318
+ * Replace anything that's not safe in a filename / npm-package-
319
+ * subpath segment. Plugin ids look like `@dogsbay/plugin-image-zoom`
320
+ * or `@dogsbay/plugin-image-zoom#1`; slashes and `@` and `#` are
321
+ * the parts we need to flatten. The result is reversible enough
322
+ * for the user to figure out which file maps to which plugin.
323
+ */
324
+ function slugifyPluginId(id) {
325
+ return id.replace(/^@/, "").replace(/\//g, "__").replace(/#/g, "_at_");
326
+ }
327
+ function writeFile(absPath, contents) {
328
+ mkdirSync(dirname(absPath), { recursive: true });
329
+ writeFileSync(absPath, contents);
330
+ }
331
+ /** Test-only: clear emitted plugin runtime files. */
332
+ export function _clearPluginRuntime(outputDir) {
333
+ for (const rel of [PLUGIN_RUNTIME_REL, PLUGIN_VITE_REL]) {
334
+ rmSync(join(outputDir, rel), { force: true });
335
+ }
336
+ rmSync(join(outputDir, PLUGIN_CONFIG_DIR_REL), { recursive: true, force: true });
337
+ rmSync(join(outputDir, PLUGIN_STYLES_DIR_REL), { recursive: true, force: true });
338
+ }
339
+ //# sourceMappingURL=plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,320 @@
1
+ import type { ExportPage, NavItem } from "@dogsbay/types";
2
+ export interface AstroProjectOptions {
3
+ siteName?: string;
4
+ theme?: string;
5
+ local?: boolean;
6
+ repoUrl?: string;
7
+ editUri?: string;
8
+ copyright?: string;
9
+ /**
10
+ * Brand keywords for the site. Emitted into `src/data/site.json`
11
+ * so the `seo/h1-brand-keyword` audit rule can read them.
12
+ */
13
+ brandKeywords?: string[];
14
+ /** Source project directory (for copying extra_css, assets, etc.) */
15
+ sourceDir?: string;
16
+ /** Custom CSS files from mkdocs.yml extra_css */
17
+ extraCss?: string[];
18
+ /** Enable Astro image optimization (copies to src/assets/ instead of public/) */
19
+ imageOptimization?: boolean;
20
+ /** Controls CodeBlock title bar: true = always (default), "auto" = only with explicit title, false = never */
21
+ codeBlockTitle?: boolean | "auto";
22
+ /** Section name — pages go under docs/{section}/, nav gets a top-level entry */
23
+ section?: string;
24
+ /**
25
+ * URL path prefix where pages are served. Default `"/docs"`. Set to
26
+ * `""` to serve at the host root, `"/handbook"` (etc.) for any
27
+ * other prefix. Threaded through every emitter that builds URLs,
28
+ * file output paths, or rewrites links — so changing this lifts
29
+ * the entire site to the new prefix in one shot. See
30
+ * `normalizeBasePath` and plans/configurable-base-path.md.
31
+ */
32
+ basePath?: string;
33
+ /**
34
+ * Path prefix for tag term pages, used when emitting the page-level
35
+ * `<TagList>` chip strip. Default `/tags` — match the indexPath
36
+ * declared in `dogsbay.config.yml`'s `taxonomies.tags` block.
37
+ */
38
+ tagsIndexPath?: string;
39
+ /**
40
+ * Per-prefix display config for tag chips (label + palette color).
41
+ * Sourced from `taxonomies.tags.prefixes`. Emitted into
42
+ * `site.json` and forwarded to `<TagList>` so chips render with
43
+ * prefix-keyed colors and two-part labels. Absent → flat chips.
44
+ * See plans/tag-display-config.md.
45
+ */
46
+ tagPrefixes?: import("@dogsbay/types").SiteConfig["tagPrefixes"];
47
+ /**
48
+ * Per-tag leaf-label overrides. Sourced from
49
+ * `taxonomies.tags.labels`. URLs continue to use slugs.
50
+ */
51
+ tagLabels?: import("@dogsbay/types").SiteConfig["tagLabels"];
52
+ /**
53
+ * Map of taxonomy name → index path, sourced from declared
54
+ * `taxonomies` entries. Lets renderer components surface a link
55
+ * from built-in field badges (`TypeBadge`, `StatusBadge`) when
56
+ * the user has opted in to a browse destination.
57
+ */
58
+ taxonomyIndexPaths?: import("@dogsbay/types").SiteConfig["taxonomyIndexPaths"];
59
+ /**
60
+ * Per-taxonomy display config (`prefixes` + `labels`), keyed by
61
+ * taxonomy name. Powers the search-facet UI; mirrors the same
62
+ * data the chip components consume but in a per-taxonomy shape.
63
+ * See plans/search-facets.md.
64
+ */
65
+ taxonomyDisplay?: import("@dogsbay/types").SiteConfig["taxonomyDisplay"];
66
+ /** Canonical site URL — required for sitemap.xml + canonical tags */
67
+ siteUrl?: string;
68
+ /** Site-wide description used as default <meta name="description"> */
69
+ description?: string;
70
+ /** Default OG image URL (1200×630 PNG/JPG) */
71
+ ogImage?: string;
72
+ /** Twitter / X handle including "@" */
73
+ twitterHandle?: string;
74
+ /** Theme color hint for browsers (hex) */
75
+ themeColor?: string;
76
+ /** Plausible domain — when set, docs-layout emits the tracker script */
77
+ plausibleDomain?: string;
78
+ /** Optional Plausible script URL override (for self-hosted) */
79
+ plausibleScriptUrl?: string;
80
+ /**
81
+ * Declared versions for the version axis. When present and ≥2,
82
+ * `emitSwitcherMap` emits `src/data/switcherMap.json` with per-
83
+ * page version equivalents and a header with this list (so the
84
+ * VersionSwitcher component can render labels + EOL marks even
85
+ * when a page is missing an equivalent in some version).
86
+ *
87
+ * See plans/multi-source-content.md and
88
+ * docs-dev/multi-source-architecture.md.
89
+ */
90
+ versions?: Array<{
91
+ id: string;
92
+ label?: string;
93
+ eol?: boolean;
94
+ }>;
95
+ /**
96
+ * Which version is the default. Marked in switcherMap.json so
97
+ * the switcher can highlight it; PR 4c wires the default-
98
+ * version redirect that uses this same field.
99
+ */
100
+ defaultVersion?: string;
101
+ /**
102
+ * Declared locales for the i18n axis. Same role as
103
+ * `versions` for the locale axis.
104
+ */
105
+ locales?: Array<{
106
+ id: string;
107
+ label?: string;
108
+ }>;
109
+ /**
110
+ * Which locale is the default. Used for the default-locale
111
+ * redirect AND the missing-translation fallback.
112
+ */
113
+ defaultLocale?: string;
114
+ /**
115
+ * When set, emit a deploy config file + dev dep + script for the
116
+ * named platform. Only "cloudflare-workers" supported today.
117
+ * Other adapters (netlify, vercel, gh-pages) follow the same shape;
118
+ * left unimplemented per Phase 1 scope.
119
+ */
120
+ deploy?: "cloudflare-workers";
121
+ /**
122
+ * Overwrite all files including maintainer-customizable scaffold
123
+ * files (theme.css, astro.config.mjs, package.json, site.json,
124
+ * tsconfig.json, wrangler.jsonc, ui component copies). Default
125
+ * behaviour: scaffold those files only on first run; on subsequent
126
+ * runs leave them alone and regenerate ONLY the auto-managed files
127
+ * (`src/pages/docs/**`, `src/data/nav.json`, `public/robots.txt`).
128
+ *
129
+ * Use this when intentionally re-scaffolding (theme upgrade, deploy
130
+ * target switch, etc.) and you've reviewed the diff.
131
+ */
132
+ force?: boolean;
133
+ /**
134
+ * Emit `public/llms.txt`, `public/llms-full.txt`, and per-section
135
+ * `public/<dir>/llms.txt` files for LLM discovery. Default `true`.
136
+ * Disable when the host enforces a different content policy or when
137
+ * the site is private / not for agent consumption.
138
+ */
139
+ llmsTxt?: boolean;
140
+ /**
141
+ * Emit a companion `<page>.md.ts` Astro endpoint for every generated
142
+ * page so agents can fetch raw Dogsbay-MD at `<page>.md`. Prerendered
143
+ * to static `.md` files in `dist/`. Default `true`.
144
+ */
145
+ mdMirror?: boolean;
146
+ /**
147
+ * `Content-Signal` directive for the AI training corpus permission
148
+ * emitted in robots.txt. Default `"no"` — opt out of training by
149
+ * default; writers explicitly opt in by passing `"yes"`.
150
+ */
151
+ aiTrain?: "yes" | "no";
152
+ /**
153
+ * `Content-Signal` directive for AI inference / agent read-time
154
+ * access emitted in robots.txt. Default `"yes"` — agents reading
155
+ * docs at user-request time is the whole point of agent-readiness.
156
+ */
157
+ aiInput?: "yes" | "no";
158
+ /**
159
+ * `Content-Signal` directive for inclusion in agent and human
160
+ * search results emitted in robots.txt. Default `"yes"`.
161
+ */
162
+ aiSearch?: "yes" | "no";
163
+ /**
164
+ * Per-page LLM action UI ("Copy as markdown", "Open in Claude/
165
+ * ChatGPT/Perplexity/Gemini"). When omitted, the cluster is not
166
+ * rendered. Independent of `mdMirror` / `llmsTxt` — those drive
167
+ * the data layer, this drives the human UI on top.
168
+ * See plans/llm-page-actions.md.
169
+ */
170
+ llmActions?: import("@dogsbay/types").SiteConfig["llmActions"];
171
+ }
172
+ /**
173
+ * Export pages + nav to a complete Astro project directory.
174
+ *
175
+ * Thin orchestrator. The heavy lifting lives in four focused emitters
176
+ * (one per emission tier) that this function calls in sequence:
177
+ *
178
+ * 1. emitSiteScaffold — Tier 2 (scaffold-once): package.json,
179
+ * theme, astro.config.mjs, components, etc.
180
+ * 2. emitAstroPages — Tier 1 (content): nav.json, .astro pages,
181
+ * .md mirror endpoints (when enabled)
182
+ * 3. emitConfigDerivedFiles — Tier 1 (config-derived): robots.txt
183
+ * 4. emitAgentReadinessFiles — Tier 1 (agent-tier): llms.txt, _headers,
184
+ * middleware.ts (when enabled)
185
+ *
186
+ * Each emitter is also callable directly — site init / site build (in
187
+ * progress) call only the subset they need.
188
+ */
189
+ export declare function exportAstroProject(pages: ExportPage[], nav: NavItem[], outputDir: string, options?: AstroProjectOptions): Promise<void>;
190
+ /**
191
+ * Emit the project scaffold: package.json, astro.config.mjs, tsconfig,
192
+ * theme/global CSS, site.json, copied UI components, deploy config.
193
+ *
194
+ * @returns the count of files skipped because the project is already
195
+ * scaffolded (used by the orchestrator's "preserved N files" log).
196
+ */
197
+ /**
198
+ * Emit `src/data/site.json` from the resolved options. Pure
199
+ * config-derived (Tier 1) — overwrites unconditionally so config
200
+ * edits in `dogsbay.config.yml` propagate to the rendered site on
201
+ * every `dogsbay site build` without needing `--force`.
202
+ *
203
+ * Standalone helper so `site build` can refresh it without going
204
+ * through the full scaffold path. `emitSiteScaffold` also calls it.
205
+ */
206
+ export declare function emitSiteConfig(outputDir: string, siteName: string, options: AstroProjectOptions): void;
207
+ export declare function emitSiteScaffold(outputDir: string, siteName: string, options: AstroProjectOptions, writeScaffold: boolean): number;
208
+ /**
209
+ * Emit content-tier files: nav.json, every page's .astro, the per-page
210
+ * .md mirror endpoint (when `mdMirror` is enabled), and the index
211
+ * redirect. Returns the count of pages emitted and the merged nav for
212
+ * downstream consumers (llms.txt builder).
213
+ */
214
+ export declare function emitAstroPages(pages: ExportPage[], nav: NavItem[], outputDir: string, options: AstroProjectOptions): Promise<{
215
+ generated: number;
216
+ outputNav: NavItem[];
217
+ }>;
218
+ /**
219
+ * Emit `public/robots.txt` with `Content-Signal` directives + sitemap
220
+ * link. Always overwrites — the file's contents are pure derivation
221
+ * from `options`. Maintainers wanting custom Disallow rules layer
222
+ * them at the CDN level.
223
+ */
224
+ export declare function emitConfigDerivedFiles(outputDir: string, options: AstroProjectOptions): void;
225
+ /**
226
+ * Shape of the emitted `src/data/switcherMap.json` file.
227
+ * Switcher components import this JSON directly, so the shape
228
+ * is also the runtime contract.
229
+ *
230
+ * Each `byLogicalKey` value is a FLAT LIST of variants — every
231
+ * (locale, version) combo where this logical page exists, with
232
+ * its full URL. Switcher components filter the list to find
233
+ * relevant alternates:
234
+ *
235
+ * - VersionSwitcher filters where `locale === currentLocale`
236
+ * (so the dropdown only crosses the version axis).
237
+ * - LocaleSwitcher filters where `version === currentVersion`.
238
+ *
239
+ * For axes that aren't active site-wide, the field is undefined
240
+ * on every variant; switchers for that axis simply render
241
+ * nothing.
242
+ */
243
+ export interface SwitcherMap {
244
+ /**
245
+ * Declared versions in declaration order. The component uses
246
+ * this to render the dropdown in canonical order even when
247
+ * some entries are missing from a given page's variants list
248
+ * (those entries render as greyed-out / "no equivalent").
249
+ */
250
+ versions: Array<{
251
+ id: string;
252
+ label?: string;
253
+ eol?: boolean;
254
+ default?: boolean;
255
+ }>;
256
+ /**
257
+ * Declared locales in declaration order. Same role as
258
+ * `versions` but for the i18n axis.
259
+ */
260
+ locales: Array<{
261
+ id: string;
262
+ label?: string;
263
+ default?: boolean;
264
+ }>;
265
+ /**
266
+ * Per-logical-key list of variants. Logical key is
267
+ * `<namespace>/<originalSlug>` (namespace defaults to "docs"
268
+ * when the namespace axis is inactive). Each variant carries
269
+ * the full URL plus the (locale, version) combo it represents.
270
+ */
271
+ byLogicalKey: Record<string, SwitcherVariant[]>;
272
+ }
273
+ export interface SwitcherVariant {
274
+ /** Present when the locale axis is active for this site. */
275
+ locale?: string;
276
+ /** Present when the version axis is active for this site. */
277
+ version?: string;
278
+ /** Full URL (with basePath) for this variant. */
279
+ url: string;
280
+ }
281
+ /**
282
+ * Emit `src/data/switcherMap.json` describing per-page
283
+ * version + locale equivalents. Always writes the file —
284
+ * even when no axis is active — with empty `versions: []`,
285
+ * `locales: []`, `byLogicalKey: {}`. Keeps the per-page
286
+ * static import working in every site, with switchers
287
+ * rendering nothing when their axis has < 2 declared entries.
288
+ *
289
+ * Pages without `multiSource` metadata are skipped — they
290
+ * aren't part of any axis, so they don't appear in any
291
+ * switcher. Pages with multiSource (any axis active) DO
292
+ * appear, even when this particular page sits in the
293
+ * default-bucket of the relevant axis (e.g. an unversioned
294
+ * baseline page in a multi-version site).
295
+ */
296
+ export declare function emitSwitcherMap(pages: ExportPage[], outputDir: string, options: AstroProjectOptions): void;
297
+ /**
298
+ * Emit redirect stub pages for missing translations. For each
299
+ * page that exists in the default locale, ensure there's a
300
+ * corresponding URL for every other declared locale: if no
301
+ * page already exists at that URL, write a stub Astro page
302
+ * that 302-redirects to the default-locale equivalent.
303
+ *
304
+ * Stubs aren't emitted FROM other-locale TO default-locale —
305
+ * only the other way. The default locale is the canonical
306
+ * baseline; if a writer authored content only in (say) `fr`,
307
+ * we don't manufacture an `en` URL with a fake redirect.
308
+ */
309
+ export declare function emitMissingTranslationStubs(pages: ExportPage[], outputDir: string, options: AstroProjectOptions): void;
310
+ /**
311
+ * Emit agent-readiness files: `public/llms.txt` + `llms-full.txt` +
312
+ * per-section indexes when `llmsTxt` is enabled; `public/_headers`
313
+ * (Cloudflare Link header) alongside; and `src/middleware.ts` when
314
+ * `mdMirror` is enabled.
315
+ *
316
+ * `.md` mirror endpoints themselves are emitted per-page inside
317
+ * `emitAstroPages` (they share the page-loop's serialization step).
318
+ */
319
+ export declare function emitAgentReadinessFiles(pages: ExportPage[], outputNav: NavItem[], outputDir: string, siteName: string, options: AstroProjectOptions): void;
320
+ //# sourceMappingURL=project.d.ts.map