@ascribed/astro 0.4.3 → 0.4.4

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.
package/README.md CHANGED
@@ -21,7 +21,7 @@ export default defineConfig({
21
21
  });
22
22
  ```
23
23
 
24
- Astro's `site`, `base`, and `trailingSlash` come from `ascribe.toml`'s `[consumer]` when the Astro config leaves them out. With `projects`, `site` and `trailingSlash` come from the first entry whose project sets them, and `base` stays the site's. Set in the Astro config, each must agree with `[consumer]`, or the build fails.
24
+ Astro's `site`, `base`, and `trailingSlash` come from `ascribe.toml`'s `[consumer]` when the Astro config leaves them out. With `projects`, `site` and `trailingSlash` come from the first entry whose project sets them, and `base` stays the site's. Set in the Astro config, each must agree with `[consumer]`, or the build fails. With `trailing-slash = "never"`, Astro's `build.format` becomes `"file"`, so each page is `x.html`, which a static host serves at `x`; a `build.format` that disagrees with `trailing-slash` fails the build.
25
25
 
26
26
  ```ts
27
27
  // src/content.config.ts
@@ -69,7 +69,7 @@ Each build writes a record of every page, `_ascribe/pages.json`, and the integra
69
69
 
70
70
  A versioned build (`versions` in `ascribe.toml`) is one collection with every live line's pages: the current line's at the base path, and an older line's under its number, `/docs/3.4/`, with the line in front of their entry ids. Each entry's data has `line` and `current`. `Versions.astro`, given a page's entry, lists the lines, each that publishes the page a link, with the page's own marked; `VersionBanner.astro` says on an older line's page which version it is and links to the current line's; `sidebar(entries, { entry })` shows the pages of the entry's line. `astro dev` rebuilds every line. A permalink forwards to the current line, and each line's `llms.txt` and Markdown are published at its base path.
71
71
 
72
- A page's Markdown version has no head, so its rules are response headers, which each build lists in `_ascribe/headers.json`: `X-Robots-Tag` when the page isn't listed, and a canonical `Link` to the page. `astro dev` sends them. With `[consumer] headers = "netlify"`, `astro build` writes them into the site's `_headers`; a site with its own `public/_headers` keeps it, and the build fails with the lines it lacks.
72
+ A page's Markdown version has no head, so its rules are response headers, which each build lists in `_ascribe/headers.json`: `X-Robots-Tag` when the page isn't listed, and a canonical `Link` to the page. `astro dev` sends them. With `[consumer] host = "netlify"`, each build writes them in Netlify's format too, at `_ascribe/host/_headers`, and `astro build` merges every project's into the site's `_headers`; a site with its own `public/_headers` keeps it, and the build fails with the lines it lacks. The same setting has `astro build` write the site's `_redirects`, from the moves each project's `ascribe.addresses` records, ahead of a site's own `public/_redirects`.
73
73
 
74
74
  `npx @ascribed/astro init` writes those files into an Astro project once, as yours from then on: the collection (`src/content.config.ts`), the route (`src/pages/[...slug].astro`), and a layout that loads the elements and draws the sidebar (`src/layouts/Docs.astro`). It never writes over a file. `--project <dir>` names the folder holding `ascribe.toml`, and `--build <name>` the build, when there are several. The layout's sidebar comes from `sidebar(entries, { entry })` in `@ascribed/astro/content`: the project's navigation, in `ascribe.nav`'s order or the default one, from the build's `_ascribe/nav.json`, with no list to keep. `neighbors(entry, entries)` gives a page's previous and next in the same order, for links at its foot. A site's own list, `sidebar(entries, { nav })`, overrides the navigation: its groups, in its order, and the build fails when the list leaves out a page or names one that isn't there; `neighbors` takes it too.
75
75
 
package/dist/headers.d.ts CHANGED
@@ -7,6 +7,12 @@ export interface HeaderRule {
7
7
  }
8
8
  /** The rules in a build's site output, or `undefined` when it has no list. */
9
9
  export declare function readHeaders(siteRoot: string): HeaderRule[] | undefined;
10
+ /**
11
+ * The rules in the host's `_headers` the compiler wrote in a build's site
12
+ * output, `_ascribe/host/_headers`, in its order: `undefined` when it has
13
+ * none, as without `[consumer] host`.
14
+ */
15
+ export declare function readHostHeaders(siteRoot: string): HeaderRule[] | undefined;
10
16
  /**
11
17
  * The headers the rules in the build whose site output is at `siteRoot` give
12
18
  * `pathname`, a path from the host's root: none when no rule names it. Read
@@ -41,7 +47,7 @@ export type HeadersOutcome = {
41
47
  };
42
48
  /** What the build does about `_headers`, given every project's rules and the site. */
43
49
  export declare function headersOutcome(sources: {
44
- /** The rules of every project that sets `[consumer] headers = "netlify"`. */
50
+ /** The rules of every project that sets `[consumer] host = "netlify"`. */
45
51
  rules: readonly HeaderRule[];
46
52
  /** Astro's `base`. */
47
53
  base: string;
package/dist/headers.js CHANGED
@@ -1,10 +1,11 @@
1
1
  // The response headers a build's files need from the host:
2
2
  // `_ascribe/headers.json` in its site output (output-layout contract §1.4).
3
3
  // A Markdown version has no head, so its page's rules are headers, which only
4
- // the host sends. With `[consumer] headers = "netlify"`, `astro build` writes
5
- // them into the site's `_headers`, Netlify's format; `astro dev` sends them
6
- // with each Markdown version whatever the setting, so a site can be tried
7
- // with no host.
4
+ // the host sends. With `[consumer] host = "netlify"`, the compiler writes them
5
+ // in Netlify's format too, at `_ascribe/host/_headers` (§1.9), and `astro
6
+ // build` merges every project's into the site's `_headers`; `astro dev` sends
7
+ // them with each Markdown version whatever the setting, so a site can be
8
+ // tried with no host.
8
9
  //
9
10
  // A site's own `public/_headers` is never rewritten: the build checks it has
10
11
  // the lines, and fails with the lines to add when it doesn't.
@@ -32,6 +33,37 @@ export function readHeaders(siteRoot) {
32
33
  }
33
34
  return list.headers;
34
35
  }
36
+ /**
37
+ * The rules in the host's `_headers` the compiler wrote in a build's site
38
+ * output, `_ascribe/host/_headers`, in its order: `undefined` when it has
39
+ * none, as without `[consumer] host`.
40
+ */
41
+ export function readHostHeaders(siteRoot) {
42
+ let text;
43
+ try {
44
+ text = readFileSync(path.join(siteRoot, "_ascribe", "host", "_headers"), "utf8");
45
+ }
46
+ catch {
47
+ return undefined;
48
+ }
49
+ const rules = [];
50
+ let current;
51
+ for (const line of text.split(/\r?\n/)) {
52
+ const trimmed = line.trim();
53
+ if (trimmed === "" || trimmed.startsWith("#"))
54
+ continue;
55
+ if (!/^\s/.test(line)) {
56
+ current = { path: trimmed, headers: {} };
57
+ rules.push(current);
58
+ continue;
59
+ }
60
+ const colon = trimmed.indexOf(":");
61
+ if (current === undefined || colon === -1)
62
+ continue;
63
+ current.headers[trimmed.slice(0, colon).trim()] = trimmed.slice(colon + 1).trim();
64
+ }
65
+ return rules;
66
+ }
35
67
  /** A build's rules by path, as last read, and when its file was written. */
36
68
  const byPath = new Map();
37
69
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"headers.js","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"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","sourcesContent":["// The response headers a build's files need from the host:\n// `_ascribe/headers.json` in its site output (output-layout contract §1.4).\n// A Markdown version has no head, so its page's rules are headers, which only\n// the host sends. With `[consumer] headers = \"netlify\"`, `astro build` writes\n// them into the site's `_headers`, Netlify's format; `astro dev` sends them\n// with each Markdown version whatever the setting, so a site can be tried\n// with no host.\n//\n// A site's own `public/_headers` is never rewritten: the build checks it has\n// the lines, and fails with the lines to add when it doesn't.\nimport { readFileSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { normalizeBase } from \"./project.js\";\n\n/** The format version of `_ascribe/headers.json` this reads. */\nconst VERSION = 1;\n\n/** The headers a host sends with one path, matched exactly. */\nexport interface HeaderRule {\n /** The path, from the host's root. */\n path: string;\n /** Each header's value, by its name. */\n headers: Record<string, string>;\n}\n\n/** `_ascribe/headers.json`. */\ninterface HeaderList {\n version: number;\n headers: HeaderRule[];\n}\n\nfunction headersFile(siteRoot: string): string {\n return path.join(siteRoot, \"_ascribe\", \"headers.json\");\n}\n\n/** The rules in a build's site output, or `undefined` when it has no list. */\nexport function readHeaders(siteRoot: string): HeaderRule[] | undefined {\n const file = headersFile(siteRoot);\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return undefined;\n }\n const list = JSON.parse(text) as HeaderList;\n if (list.version !== VERSION) {\n throw new Error(\n `@ascribed/astro: ${file} is version ${String(list.version)} of the headers list, and this version of the integration reads version ${VERSION}. Update @ascribed/astro and @ascribed/cli together.`,\n );\n }\n return list.headers;\n}\n\n/** A build's rules by path, as last read, and when its file was written. */\nconst byPath = new Map<string, { written: number; rules: Map<string, Record<string, string>> }>();\n\n/**\n * The headers the rules in the build whose site output is at `siteRoot` give\n * `pathname`, a path from the host's root: none when no rule names it. Read\n * again when the file changes, as it does with each build in `astro dev`.\n */\nexport function headersFor(siteRoot: string, pathname: string): Record<string, string> {\n let written: number;\n try {\n written = statSync(headersFile(siteRoot)).mtimeMs;\n } catch {\n return {};\n }\n let known = byPath.get(siteRoot);\n if (known === undefined || known.written !== written) {\n const rules = new Map<string, Record<string, string>>();\n for (const rule of readHeaders(siteRoot) ?? []) {\n if (!rules.has(rule.path)) rules.set(rule.path, rule.headers);\n }\n known = { written, rules };\n byPath.set(siteRoot, known);\n }\n return known.rules.get(pathname) ?? {};\n}\n\n/**\n * Every project's rules as one list, sorted by path: a path two projects\n * both have keeps the first's rule, as the first entry's files win.\n */\nexport function mergeRules(projects: readonly (readonly HeaderRule[])[]): HeaderRule[] {\n const rules = new Map<string, HeaderRule>();\n for (const list of projects) {\n for (const rule of list) if (!rules.has(rule.path)) rules.set(rule.path, rule);\n }\n return [...rules.values()].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n}\n\n/** The rules in Netlify's `_headers` format: each path, then its headers indented below it. */\nexport function netlifyHeaders(rules: readonly HeaderRule[]): string {\n return rules\n .map(\n (rule) =>\n `${rule.path}\\n${Object.entries(rule.headers)\n .map(([name, value]) => ` ${name}: ${value}\\n`)\n .join(\"\")}`,\n )\n .join(\"\");\n}\n\n/**\n * The headers `_headers` gives each path exactly, as Netlify reads the file:\n * a line that isn't indented is a path, an indented `Name: value` line is a\n * header of the path above it, and a line starting with `#` is a comment.\n * Names are compared in lower case, as Netlify compares them. A path with a\n * wildcard or a placeholder is kept as written, and matches only itself here.\n */\nfunction parseHeaders(text: string): Map<string, Set<string>> {\n const paths = new Map<string, Set<string>>();\n let current: Set<string> | undefined;\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (trimmed === \"\" || trimmed.startsWith(\"#\")) continue;\n if (!/^\\s/.test(line)) {\n current = paths.get(trimmed) ?? new Set();\n paths.set(trimmed, current);\n continue;\n }\n const colon = trimmed.indexOf(\":\");\n if (current === undefined || colon === -1) continue;\n current.add(headerKey(trimmed.slice(0, colon), trimmed.slice(colon + 1)));\n }\n return paths;\n}\n\nfunction headerKey(name: string, value: string): string {\n return `${name.trim().toLowerCase()}: ${value.trim()}`;\n}\n\n/**\n * The rules, each with only the headers the site's own `_headers` doesn't\n * give its path, leaving out a rule it has whole. Empty when it has every\n * line.\n */\nexport function missingRules(own: string, rules: readonly HeaderRule[]): HeaderRule[] {\n const has = parseHeaders(own);\n const missing: HeaderRule[] = [];\n for (const rule of rules) {\n const given = has.get(rule.path);\n const headers = Object.fromEntries(\n Object.entries(rule.headers).filter(([name, value]) => !given?.has(headerKey(name, value))),\n );\n if (Object.keys(headers).length > 0) missing.push({ path: rule.path, headers });\n }\n return missing;\n}\n\n/** What the build does about the host's headers. */\nexport type HeadersOutcome =\n | { kind: \"none\" }\n | { kind: \"write\"; text: string }\n | { kind: \"note\"; text: string }\n | { kind: \"fail\"; text: string };\n\n/** What the build does about `_headers`, given every project's rules and the site. */\nexport function headersOutcome(sources: {\n /** The rules of every project that sets `[consumer] headers = \"netlify\"`. */\n rules: readonly HeaderRule[];\n /** Astro's `base`. */\n base: string;\n /** The site's own `public/_headers`, when it has one. */\n own: string | undefined;\n}): HeadersOutcome {\n if (sources.rules.length === 0) return { kind: \"none\" };\n const text = netlifyHeaders(sources.rules);\n const base = normalizeBase(sources.base);\n if (base !== \"/\") {\n return {\n kind: \"note\",\n text: `no _headers: Astro's \\`base\\` is \"${base}\", and Netlify reads _headers only at the root of what's deployed. Put these lines in the host's own:\\n${text}`,\n };\n }\n if (sources.own !== undefined) {\n const missing = missingRules(sources.own, sources.rules);\n if (missing.length === 0) {\n return {\n kind: \"note\",\n text: \"no _headers: public/_headers is the site's own, and has Ascribe's lines.\",\n };\n }\n return {\n kind: \"fail\",\n text: `public/_headers is the site's own, and lacks the headers Ascribe's pages need. Add these lines to it:\\n${netlifyHeaders(missing)}`,\n };\n }\n return { kind: \"write\", text };\n}\n"]}
1
+ {"version":3,"file":"headers.js","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"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","sourcesContent":["// The response headers a build's files need from the host:\n// `_ascribe/headers.json` in its site output (output-layout contract §1.4).\n// A Markdown version has no head, so its page's rules are headers, which only\n// the host sends. With `[consumer] host = \"netlify\"`, the compiler writes them\n// in Netlify's format too, at `_ascribe/host/_headers` (§1.9), and `astro\n// build` merges every project's into the site's `_headers`; `astro dev` sends\n// them with each Markdown version whatever the setting, so a site can be\n// tried with no host.\n//\n// A site's own `public/_headers` is never rewritten: the build checks it has\n// the lines, and fails with the lines to add when it doesn't.\nimport { readFileSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { normalizeBase } from \"./project.js\";\n\n/** The format version of `_ascribe/headers.json` this reads. */\nconst VERSION = 1;\n\n/** The headers a host sends with one path, matched exactly. */\nexport interface HeaderRule {\n /** The path, from the host's root. */\n path: string;\n /** Each header's value, by its name. */\n headers: Record<string, string>;\n}\n\n/** `_ascribe/headers.json`. */\ninterface HeaderList {\n version: number;\n headers: HeaderRule[];\n}\n\nfunction headersFile(siteRoot: string): string {\n return path.join(siteRoot, \"_ascribe\", \"headers.json\");\n}\n\n/** The rules in a build's site output, or `undefined` when it has no list. */\nexport function readHeaders(siteRoot: string): HeaderRule[] | undefined {\n const file = headersFile(siteRoot);\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return undefined;\n }\n const list = JSON.parse(text) as HeaderList;\n if (list.version !== VERSION) {\n throw new Error(\n `@ascribed/astro: ${file} is version ${String(list.version)} of the headers list, and this version of the integration reads version ${VERSION}. Update @ascribed/astro and @ascribed/cli together.`,\n );\n }\n return list.headers;\n}\n\n/**\n * The rules in the host's `_headers` the compiler wrote in a build's site\n * output, `_ascribe/host/_headers`, in its order: `undefined` when it has\n * none, as without `[consumer] host`.\n */\nexport function readHostHeaders(siteRoot: string): HeaderRule[] | undefined {\n let text: string;\n try {\n text = readFileSync(path.join(siteRoot, \"_ascribe\", \"host\", \"_headers\"), \"utf8\");\n } catch {\n return undefined;\n }\n const rules: HeaderRule[] = [];\n let current: HeaderRule | undefined;\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (trimmed === \"\" || trimmed.startsWith(\"#\")) continue;\n if (!/^\\s/.test(line)) {\n current = { path: trimmed, headers: {} };\n rules.push(current);\n continue;\n }\n const colon = trimmed.indexOf(\":\");\n if (current === undefined || colon === -1) continue;\n current.headers[trimmed.slice(0, colon).trim()] = trimmed.slice(colon + 1).trim();\n }\n return rules;\n}\n\n/** A build's rules by path, as last read, and when its file was written. */\nconst byPath = new Map<string, { written: number; rules: Map<string, Record<string, string>> }>();\n\n/**\n * The headers the rules in the build whose site output is at `siteRoot` give\n * `pathname`, a path from the host's root: none when no rule names it. Read\n * again when the file changes, as it does with each build in `astro dev`.\n */\nexport function headersFor(siteRoot: string, pathname: string): Record<string, string> {\n let written: number;\n try {\n written = statSync(headersFile(siteRoot)).mtimeMs;\n } catch {\n return {};\n }\n let known = byPath.get(siteRoot);\n if (known === undefined || known.written !== written) {\n const rules = new Map<string, Record<string, string>>();\n for (const rule of readHeaders(siteRoot) ?? []) {\n if (!rules.has(rule.path)) rules.set(rule.path, rule.headers);\n }\n known = { written, rules };\n byPath.set(siteRoot, known);\n }\n return known.rules.get(pathname) ?? {};\n}\n\n/**\n * Every project's rules as one list, sorted by path: a path two projects\n * both have keeps the first's rule, as the first entry's files win.\n */\nexport function mergeRules(projects: readonly (readonly HeaderRule[])[]): HeaderRule[] {\n const rules = new Map<string, HeaderRule>();\n for (const list of projects) {\n for (const rule of list) if (!rules.has(rule.path)) rules.set(rule.path, rule);\n }\n return [...rules.values()].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n}\n\n/** The rules in Netlify's `_headers` format: each path, then its headers indented below it. */\nexport function netlifyHeaders(rules: readonly HeaderRule[]): string {\n return rules\n .map(\n (rule) =>\n `${rule.path}\\n${Object.entries(rule.headers)\n .map(([name, value]) => ` ${name}: ${value}\\n`)\n .join(\"\")}`,\n )\n .join(\"\");\n}\n\n/**\n * The headers `_headers` gives each path exactly, as Netlify reads the file:\n * a line that isn't indented is a path, an indented `Name: value` line is a\n * header of the path above it, and a line starting with `#` is a comment.\n * Names are compared in lower case, as Netlify compares them. A path with a\n * wildcard or a placeholder is kept as written, and matches only itself here.\n */\nfunction parseHeaders(text: string): Map<string, Set<string>> {\n const paths = new Map<string, Set<string>>();\n let current: Set<string> | undefined;\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (trimmed === \"\" || trimmed.startsWith(\"#\")) continue;\n if (!/^\\s/.test(line)) {\n current = paths.get(trimmed) ?? new Set();\n paths.set(trimmed, current);\n continue;\n }\n const colon = trimmed.indexOf(\":\");\n if (current === undefined || colon === -1) continue;\n current.add(headerKey(trimmed.slice(0, colon), trimmed.slice(colon + 1)));\n }\n return paths;\n}\n\nfunction headerKey(name: string, value: string): string {\n return `${name.trim().toLowerCase()}: ${value.trim()}`;\n}\n\n/**\n * The rules, each with only the headers the site's own `_headers` doesn't\n * give its path, leaving out a rule it has whole. Empty when it has every\n * line.\n */\nexport function missingRules(own: string, rules: readonly HeaderRule[]): HeaderRule[] {\n const has = parseHeaders(own);\n const missing: HeaderRule[] = [];\n for (const rule of rules) {\n const given = has.get(rule.path);\n const headers = Object.fromEntries(\n Object.entries(rule.headers).filter(([name, value]) => !given?.has(headerKey(name, value))),\n );\n if (Object.keys(headers).length > 0) missing.push({ path: rule.path, headers });\n }\n return missing;\n}\n\n/** What the build does about the host's headers. */\nexport type HeadersOutcome =\n | { kind: \"none\" }\n | { kind: \"write\"; text: string }\n | { kind: \"note\"; text: string }\n | { kind: \"fail\"; text: string };\n\n/** What the build does about `_headers`, given every project's rules and the site. */\nexport function headersOutcome(sources: {\n /** The rules of every project that sets `[consumer] host = \"netlify\"`. */\n rules: readonly HeaderRule[];\n /** Astro's `base`. */\n base: string;\n /** The site's own `public/_headers`, when it has one. */\n own: string | undefined;\n}): HeadersOutcome {\n if (sources.rules.length === 0) return { kind: \"none\" };\n const text = netlifyHeaders(sources.rules);\n const base = normalizeBase(sources.base);\n if (base !== \"/\") {\n return {\n kind: \"note\",\n text: `no _headers: Astro's \\`base\\` is \"${base}\", and Netlify reads _headers only at the root of what's deployed. Put these lines in the host's own:\\n${text}`,\n };\n }\n if (sources.own !== undefined) {\n const missing = missingRules(sources.own, sources.rules);\n if (missing.length === 0) {\n return {\n kind: \"note\",\n text: \"no _headers: public/_headers is the site's own, and has Ascribe's lines.\",\n };\n }\n return {\n kind: \"fail\",\n text: `public/_headers is the site's own, and lacks the headers Ascribe's pages need. Add these lines to it:\\n${netlifyHeaders(missing)}`,\n };\n }\n return { kind: \"write\", text };\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -62,7 +62,7 @@ export type AscribeOptions = OneProjectOptions | ProjectsOptions;
62
62
  * Runs `ascribe build` for each project, checks the site's routing, adds the markdown plugin, adds a
63
63
  * page for each permalink's address and an answer for each line's help entries, and serves published files, and, with `[consumer] agents = true`,
64
64
  * `llms.txt` and each page's Markdown, each at its project's base path; and writes the site's one
65
- * sitemap and `robots.txt` from every project's page records, and, with `[consumer] headers =
66
- * "netlify"`, its `_headers` from their headers lists.
65
+ * sitemap and `robots.txt` from every project's page records, and, with `[consumer] host =
66
+ * "netlify"`, its `_headers` and `_redirects` from the host's files each build writes.
67
67
  */
68
68
  export default function ascribe(options: AscribeOptions): AstroIntegration;
package/dist/index.js CHANGED
@@ -49,10 +49,11 @@ import { codeTitles } from "./code-titles.js";
49
49
  import { SeenFiles, watchDev } from "./dev.js";
50
50
  import { agentsMiddleware, copyAgentFiles, copyPublishedFiles, filesMiddleware, folderUnder, inOrder, } from "./files.js";
51
51
  import { WARN as HEAD_WARN } from "./head-check.js";
52
- import { headersFor, headersOutcome, mergeRules, readHeaders } from "./headers.js";
52
+ import { headersFor, headersOutcome, mergeRules, readHostHeaders } from "./headers.js";
53
+ import { gonePatterns, goneRoutes, mergeRedirects, readHostRedirects, redirectsOutcome, } from "./redirects.js";
53
54
  import { helpPatterns } from "./help.js";
54
55
  import { forwardingRoutes, hasPermalinks, permalinkPattern, readPermalinks } from "./permalinks.js";
55
- import { consumerMismatches, homeRoute, normalizeBase, readProject, supplyRouting, } from "./project.js";
56
+ import { consumerMismatches, homeRoute, normalizeBase, readProject, supplyBuildFormat, supplyRouting, } from "./project.js";
56
57
  import { basePathClashes, nameProblems, pagesUnderOtherPaths, permalinkProblems, } from "./projects.js";
57
58
  import { pageList, readRecords, routeAs, routeKey, sitemapRoute, } from "./records.js";
58
59
  import rehypeAscribeAttributes from "./rehype.js";
@@ -80,8 +81,8 @@ function fail(problems, what) {
80
81
  * Runs `ascribe build` for each project, checks the site's routing, adds the markdown plugin, adds a
81
82
  * page for each permalink's address and an answer for each line's help entries, and serves published files, and, with `[consumer] agents = true`,
82
83
  * `llms.txt` and each page's Markdown, each at its project's base path; and writes the site's one
83
- * sitemap and `robots.txt` from every project's page records, and, with `[consumer] headers =
84
- * "netlify"`, its `_headers` from their headers lists.
84
+ * sitemap and `robots.txt` from every project's page records, and, with `[consumer] host =
85
+ * "netlify"`, its `_headers` and `_redirects` from the host's files each build writes.
85
86
  */
86
87
  export default function ascribe(options) {
87
88
  /** With `projects`; otherwise the one entry has no name a collection gives. */
@@ -124,6 +125,7 @@ export default function ascribe(options) {
124
125
  sitePages,
125
126
  excluded: [
126
127
  ...forwardingRoutes(entries.map((entry) => readPermalinks(entry.siteRoot))),
128
+ ...goneRoutes(entries.map((entry) => entry.siteRoot)),
127
129
  `${root}404`,
128
130
  `${root}500`,
129
131
  ...(options.sitemap?.exclude ?? []),
@@ -176,6 +178,9 @@ export default function ascribe(options) {
176
178
  ? `"${entry.name}"'s ascribe.toml and astro.config disagree`
177
179
  : "ascribe.toml and astro.config disagree");
178
180
  }
181
+ // Each page's file must be served at the route Ascribe publishes for it, not behind a redirect.
182
+ const format = supplyBuildFormat(astro.trailingSlash, config.build.format);
183
+ fail(format.problems, "ascribe.toml and astro.config disagree");
179
184
  fail(basePathClashes(entries), "two projects share a base path");
180
185
  if (typeof options.review === "string" && !entries.some((e) => e.name === options.review)) {
181
186
  throw new Error(`@ascribed/astro: \`review\` names "${options.review}", and no entry of \`projects\` has that name.`);
@@ -312,6 +317,15 @@ export default function ascribe(options) {
312
317
  prerender: true,
313
318
  });
314
319
  }
320
+ // Each retired address's page, which the host's `_redirects` serves at the address with
321
+ // a 404. One route for each folder: each build's base path's.
322
+ for (const pattern of gonePatterns(entries.map((entry) => entry.siteRoot), astro.base)) {
323
+ injectRoute({
324
+ pattern,
325
+ entrypoint: new URL("../src/Gone.astro", import.meta.url),
326
+ prerender: true,
327
+ });
328
+ }
315
329
  // Each permalink's help, at `<name>.json` beside its address, and each older line's
316
330
  // under the line's base path. One route for each folder: the current line's, then each
317
331
  // older line's.
@@ -327,6 +341,7 @@ export default function ascribe(options) {
327
341
  // Dev serving of `_ascribe/files/`, and of the plain output for agents; the build copies them in `astro:build:done`.
328
342
  updateConfig({
329
343
  ...supplied,
344
+ ...(format.supplied === undefined ? {} : { build: { format: format.supplied } }),
330
345
  ...(options.codeTitles === false
331
346
  ? {}
332
347
  : { markdown: { shikiConfig: { transformers: [codeTitles] } } }),
@@ -490,12 +505,17 @@ export default function ascribe(options) {
490
505
  await writeFile(path.join(out, "robots.txt"), files.robots);
491
506
  logger.info("wrote /robots.txt");
492
507
  }
493
- // The host's headers, from the projects that name its format.
508
+ // The host's files, from the projects that name it: each build's,
509
+ // and each of its older lines'.
510
+ const hostRoots = entries
511
+ .filter((entry) => entry.project.consumer.host === "netlify")
512
+ .flatMap((entry) => [
513
+ entry.siteRoot,
514
+ ...olderLines(entry.siteRoot).map((line) => line.siteRoot),
515
+ ]);
494
516
  const ownHeaders = path.join(publicDir, "_headers");
495
517
  const headers = headersOutcome({
496
- rules: mergeRules(entries
497
- .filter((entry) => entry.project.consumer.headers === "netlify")
498
- .flatMap((entry) => [entry.siteRoot, ...olderLines(entry.siteRoot).map((line) => line.siteRoot)].map((siteRoot) => readHeaders(siteRoot) ?? []))),
518
+ rules: mergeRules(hostRoots.map((siteRoot) => readHostHeaders(siteRoot) ?? [])),
499
519
  base,
500
520
  own: existsSync(ownHeaders) ? readFileSync(ownHeaders, "utf8") : undefined,
501
521
  });
@@ -507,6 +527,21 @@ export default function ascribe(options) {
507
527
  await writeFile(path.join(out, "_headers"), headers.text);
508
528
  logger.info("wrote /_headers");
509
529
  }
530
+ const ownRedirects = path.join(publicDir, "_redirects");
531
+ const redirects = redirectsOutcome({
532
+ redirects: mergeRedirects(hostRoots.map(readHostRedirects)),
533
+ base,
534
+ published: (route) => builtKeys.has(routeKey(route)),
535
+ own: existsSync(ownRedirects) ? readFileSync(ownRedirects, "utf8") : undefined,
536
+ });
537
+ if (redirects.kind === "fail")
538
+ throw new Error(`@ascribed/astro: ${redirects.text}`);
539
+ if (redirects.kind === "note")
540
+ logger.info(redirects.text);
541
+ if (redirects.kind === "write") {
542
+ await writeFile(path.join(out, "_redirects"), redirects.text);
543
+ logger.info("wrote /_redirects");
544
+ }
510
545
  // Entries of one project publish at one base path: the first entry's copies go last, so they win.
511
546
  for (const entry of [...entries].reverse()) {
512
547
  const folder = folderUnder(entry.project.consumer.basePath, base);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// @ascribed/astro: the Astro integration for Ascribe. Written against Astro\n// 7.3.5.\n//\n// // astro.config.mjs\n// import ascribe from \"@ascribed/astro\";\n// export default defineConfig({\n// // `site`, `base`, and `trailingSlash` come from ascribe.toml's\n// // [consumer] when they're left out here; set here, they must agree.\n// integrations: [ascribe({ build: \"site\" })],\n// });\n//\n// // src/content.config.ts\n// import { defineCollection } from \"astro:content\";\n// import { ascribeCollection } from \"@ascribed/astro/content\";\n// // The generated schema. With the project outside the Astro root, import the\n// // copy inside it instead: \"../.astro/integrations/_ascribed_astro/schema.ts\".\n// import { schema } from \"../.ascribe/build/site/site/_ascribe/schema.ts\";\n// export const collections = { docs: defineCollection(ascribeCollection({ schema })) };\n//\n// // a layout\n// import Elements from \"@ascribed/astro/Elements.astro\"; // <Elements /> in <head>\n// import Availability from \"@ascribed/astro/Availability.astro\"; // the page's availability\n// import Head from \"@ascribed/astro/Head.astro\"; // <Head entry={entry} /> in <head>\n// import Versions from \"@ascribed/astro/Versions.astro\"; // the lines that have the page\n//\n// // the sidebar: the project's navigation, joined to the collection\n// import { sidebar } from \"@ascribed/astro/content\";\n// const groups = sidebar(await getCollection(\"docs\"));\n//\n// // src/pages/404.astro\n// import MissingPermalink from \"@ascribed/astro/MissingPermalink.astro\"; // a permalink that isn't here\n//\n// Several projects in one site, each entry named, and each collection naming its entry:\n//\n// ascribe({\n// projects: [\n// { name: \"docs\", project: \"../docs\", build: \"site\" },\n// { name: \"home\", project: \"home\", build: \"site\" },\n// ],\n// review: \"docs\",\n// })\n// ascribeCollection({ schema, name: \"docs\" })\nimport { existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AstroIntegration, IntegrationResolvedRoute } from \"astro\";\nimport { findBinary } from \"./binary.js\";\nimport { codeTitles } from \"./code-titles.js\";\nimport { SeenFiles, watchDev, type DevBuilds } from \"./dev.js\";\nimport {\n agentsMiddleware,\n copyAgentFiles,\n copyPublishedFiles,\n filesMiddleware,\n folderUnder,\n inOrder,\n} from \"./files.js\";\nimport { WARN as HEAD_WARN } from \"./head-check.js\";\nimport { headersFor, headersOutcome, mergeRules, readHeaders } from \"./headers.js\";\nimport { helpPatterns } from \"./help.js\";\nimport { forwardingRoutes, hasPermalinks, permalinkPattern, readPermalinks } from \"./permalinks.js\";\nimport {\n consumerMismatches,\n homeRoute,\n normalizeBase,\n readProject,\n supplyRouting,\n type AstroRouting,\n} from \"./project.js\";\nimport {\n basePathClashes,\n nameProblems,\n pagesUnderOtherPaths,\n permalinkProblems,\n type ProjectEntry,\n type ResolvedEntry,\n} from \"./projects.js\";\nimport {\n pageList,\n readRecords,\n routeAs,\n routeKey,\n sitemapRoute,\n type ProjectRobots,\n} from \"./records.js\";\nimport rehypeAscribeAttributes from \"./rehype.js\";\nimport { removeDevFile, siteUrl, writeDevFile } from \"./review/devfile.js\";\nimport { runDiff, runPrompt } from \"./review/diff.js\";\nimport { APP_ID } from \"./review/protocol.js\";\nimport { readRoutes, type RoutedPage } from \"./review/routes.js\";\nimport { channelProblem, ReviewServer, type ToolbarChannel } from \"./review/server.js\";\nimport {\n devRouted,\n devSitePages,\n rootFiles,\n rootFilesMiddleware,\n type RootFileSources,\n} from \"./root-files.js\";\nimport { anchorsFor, hasWarnings, reviewFor, runBuild } from \"./run.js\";\nimport { satteriAscribeAttributes } from \"./satteri.js\";\nimport { copySchema } from \"./schema.js\";\nimport { linesOf, olderLines } from \"./versions.js\";\n\nexport { default as rehypeAscribeAttributes } from \"./rehype.js\";\nexport { satteriAscribeAttributes } from \"./satteri.js\";\nexport type { ProjectEntry } from \"./projects.js\";\n\n/** The virtual module `content.ts` reads the resolved site output roots from. */\nconst SITE_MODULE = \"virtual:ascribe/site\";\n\n/** Options every form of the integration takes. */\ninterface SharedOptions {\n /**\n * The `ascribe` binary, relative to the Astro root. Default: `ASCRIBE_BIN`, then\n * the platform binary installed with `@ascribed/cli`.\n */\n binary?: string;\n /**\n * Mark each block of the site output with the source lines it came from\n * (source anchors), for review: `\"dev\"` in `astro dev` only, `true` always.\n * Default: `false`, though `review` has them in `astro dev`.\n */\n anchors?: boolean | \"dev\";\n /**\n * Review in the site preview: an **Ascribe review** app in `astro dev`'s\n * toolbar, which marks the page's changes and shows the pull request's\n * comments, with source anchors in `astro dev`. Default `true`; `false`\n * removes the app. `astro build` never has it. With several projects, it\n * reviews one: the entry a name names, or the first for `true`.\n */\n review?: boolean | string;\n /**\n * Show a code block's title (`title=\"…\"` in its info string, as `@snippet`\n * writes it), and what a tested example was tested with (`tested=\"…\"`),\n * above it, with a Shiki transformer that puts the block in a\n * `<figure class=\"code-title\">` with a `<figcaption>`. Default `true`.\n */\n codeTitles?: boolean;\n /**\n * The site's one sitemap, at `<base>sitemap.xml`, of every listed page of\n * every project and every page of the site's own. `exclude` lists routes,\n * from the host's root, of the site's own pages to leave out of it, such\n * as one that carries `noindex`.\n */\n sitemap?: { exclude?: string[] };\n}\n\n/** Options of the integration with one project. */\nexport interface OneProjectOptions extends SharedOptions {\n /** The build whose site output is the collection (a name in `ascribe.toml`). */\n build: string;\n /** The directory holding `ascribe.toml`, relative to the Astro root. Default: the root. */\n project?: string;\n projects?: never;\n}\n\n/** Options of the integration with several projects, each entry named. */\nexport interface ProjectsOptions extends SharedOptions {\n /** The projects, each a named build of a project; two entries may share a project. */\n projects: ProjectEntry[];\n build?: never;\n project?: never;\n}\n\n/** Options of the integration. */\nexport type AscribeOptions = OneProjectOptions | ProjectsOptions;\n\n/** The toolbar app's icon: a speech bubble over a page. */\nconst ICON =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 3h8l4 4v6\"/><path d=\"M14 3v4h4\"/><path d=\"M6 3v15\"/><path d=\"M11 14h10v6h-6l-3 2v-2h-1z\"/></svg>';\n\n/** An entry as the hooks use it. */\ninterface Entry extends ResolvedEntry {\n siteRoot: string;\n /** The plain output's root, when the project publishes it for agents. */\n agentsRoot: string | undefined;\n /** Where the generated schema's copy goes, inside the Astro root. */\n schemaDir: string;\n}\n\n/** A project with the entries that build it, which a dev watcher follows together. */\ninterface DevGroup {\n project: ResolvedEntry[\"project\"];\n entries: Entry[];\n rebuild: () => Promise<void>;\n builds?: DevBuilds;\n}\n\nfunction fail(problems: string[], what: string): void {\n if (problems.length > 0) throw new Error(`@ascribed/astro: ${what}:\\n- ${problems.join(\"\\n- \")}`);\n}\n\n/**\n * Runs `ascribe build` for each project, checks the site's routing, adds the markdown plugin, adds a\n * page for each permalink's address and an answer for each line's help entries, and serves published files, and, with `[consumer] agents = true`,\n * `llms.txt` and each page's Markdown, each at its project's base path; and writes the site's one\n * sitemap and `robots.txt` from every project's page records, and, with `[consumer] headers =\n * \"netlify\"`, its `_headers` from their headers lists.\n */\nexport default function ascribe(options: AscribeOptions): AstroIntegration {\n /** With `projects`; otherwise the one entry has no name a collection gives. */\n const named = options.projects !== undefined;\n let entries: Entry[] = [];\n let groups: DevGroup[] = [];\n let routing: AstroRouting | undefined;\n /** The dev server's sources as its first build read them. */\n const seen = new SeenFiles();\n let binary: string | undefined;\n let reviewed: Entry | undefined;\n let devCommand = false;\n let devHttps = false;\n let review: ReviewServer | undefined;\n /** The site's routes, as Astro resolved them, for what `astro dev` serves of the site's own. */\n let astroRoutes: IntegrationResolvedRoute[] = [];\n /** The site's `public/` folder, where its own `robots.txt` would be. */\n let publicDir = \"\";\n\n /** What the sitemap and `robots.txt` are drawn from, given the site's pages and its routes. */\n const rootSources = (\n sitePages: readonly string[],\n routed: (route: string) => boolean,\n ): RootFileSources => {\n const base = routing?.base ?? \"/\";\n const records = entries.map((entry) => readRecords(entry.siteRoot));\n // Entries of one project share its `ascribe.toml`, and so what it says to crawlers.\n const robots = new Map<string, ProjectRobots>();\n for (const [i, entry] of entries.entries()) {\n if (!robots.has(entry.project.dir))\n robots.set(entry.project.dir, {\n name: entry.name,\n robots: records[i]?.robots ?? { lines: [] },\n });\n }\n const root = normalizeBase(base);\n return {\n projects: [\n ...records.map(pageList),\n // An older line's pages are the site's too: those the current line has point at it.\n ...entries.flatMap((entry) =>\n olderLines(entry.siteRoot).map((line) => pageList(readRecords(line.siteRoot))),\n ),\n ],\n routed,\n sitePages,\n excluded: [\n ...forwardingRoutes(entries.map((entry) => readPermalinks(entry.siteRoot))),\n `${root}404`,\n `${root}500`,\n ...(options.sitemap?.exclude ?? []),\n ],\n robots: [...robots.values()],\n site: routing?.site,\n base,\n ownRobots: existsSync(path.join(publicDir, \"robots.txt\")),\n ownSitemap: existsSync(path.join(publicDir, \"sitemap.xml\")),\n };\n };\n return {\n name: \"@ascribed/astro\",\n hooks: {\n \"astro:config:setup\": async ({\n config,\n command,\n logger,\n updateConfig,\n addDevToolbarApp,\n createCodegenDir,\n injectRoute,\n addMiddleware,\n }) => {\n const root = fileURLToPath(config.root);\n publicDir = fileURLToPath(config.publicDir);\n const list: ProjectEntry[] = options.projects ?? [\n {\n name: \"\",\n build: options.build,\n ...(options.project === undefined ? {} : { project: options.project }),\n },\n ];\n if (named) fail(nameProblems(list), \"the `projects` option is wrong\");\n const codegen = command === \"preview\" ? \"\" : fileURLToPath(createCodegenDir());\n entries = list.map((entry) => {\n const project = readProject(path.resolve(root, entry.project ?? \".\"), {\n binary: options.binary,\n root,\n });\n return {\n name: entry.name,\n build: entry.build,\n project,\n // An unknown build is `ascribe build`'s to report: it knows the implicit `site` build.\n siteRoot: project.siteRoot(entry.build),\n agentsRoot: project.consumer.agents ? project.plainRoot(entry.build) : undefined,\n schemaDir: named ? path.join(codegen, entry.name) : codegen,\n };\n });\n\n // What astro.config leaves unset comes from [consumer]: `site` and `trailingSlash` from the\n // first project that has them, and with one project, `base` from its base path. With\n // `projects`, `base` stays the site's, the root every base path is under.\n const { routing: astro, supplied } = supplyRouting(\n entries.map((entry) => entry.project),\n { base: config.base, trailingSlash: config.trailingSlash, site: config.site },\n !named,\n );\n // `ascribe.toml`'s routing must be Astro's, or every link Ascribe writes is wrong.\n for (const entry of entries) {\n fail(\n consumerMismatches(entry.project, astro),\n named\n ? `\"${entry.name}\"'s ascribe.toml and astro.config disagree`\n : \"ascribe.toml and astro.config disagree\",\n );\n }\n fail(basePathClashes(entries), \"two projects share a base path\");\n\n if (typeof options.review === \"string\" && !entries.some((e) => e.name === options.review)) {\n throw new Error(\n `@ascribed/astro: \\`review\\` names \"${options.review}\", and no entry of \\`projects\\` has that name.`,\n );\n }\n if (reviewFor(options.review, command)) {\n reviewed =\n typeof options.review === \"string\"\n ? entries.find((e) => e.name === options.review)\n : entries[0];\n }\n\n // Where a markdown plugin goes depends on the processor Astro 7.3 runs.\n const processor = config.markdown.processor;\n const pluginLists = processor.options as {\n rehypePlugins?: unknown[];\n hastPlugins?: unknown[];\n };\n if (processor.name === \"satteri\" && pluginLists.hastPlugins !== undefined) {\n pluginLists.hastPlugins.push(satteriAscribeAttributes());\n } else if (processor.name === \"unified\" && pluginLists.rehypePlugins !== undefined) {\n pluginLists.rehypePlugins.push(rehypeAscribeAttributes);\n } else {\n throw new Error(\n `@ascribed/astro: the markdown processor \"${processor.name}\" is not one it can add its plugin to. ` +\n \"Use Astro's default (satteri) or unified(), or add `rehypeAscribeAttributes` to your own.\",\n );\n }\n\n routing = astro;\n\n if (reviewed !== undefined) {\n addDevToolbarApp({\n id: APP_ID,\n name: \"Ascribe review\",\n icon: ICON,\n entrypoint: new URL(\"./toolbar/app.js\", import.meta.url),\n });\n }\n\n if (command !== \"preview\") {\n const found = findBinary({ binary: options.binary, root });\n binary = found;\n const buildEntry = async (entry: Entry) => {\n logger.info(\n `running ${path.basename(found)} build for \"${entry.build}\"${named ? ` (${entry.name})` : \"\"}`,\n );\n const run = {\n binary: found,\n configPath: entry.project.configPath,\n cwd: entry.project.dir,\n anchors: anchorsFor(options.anchors, command, entry === reviewed),\n };\n // `llms.txt` and each page's Markdown, which agents read.\n const plain = entry.agentsRoot === undefined ? [] : ([\"plain\"] as const);\n const result = await runBuild({\n ...run,\n build: entry.build,\n outputs: [\n \"site\",\n ...plain,\n // While review is on, the JSON output says which page is at each route.\n ...(entry === reviewed && review?.active ? ([\"json\"] as const) : []),\n ],\n });\n if (hasWarnings(result.diagnostics)) logger.warn(result.diagnostics);\n else if (result.diagnostics !== \"\") logger.info(result.diagnostics);\n if (result.summary !== \"\") logger.info(result.summary);\n // A versioned build's other lines, which the build's `versions.json` names, once the\n // current line's build has written it. An error in one fails this run as it would the\n // first; `ascribe check` reports every line's warnings, so only what was built is said.\n const older = olderLines(entry.siteRoot).map((line) => line.build);\n if (older.length > 0) {\n const lines = await runBuild({\n ...run,\n build: older,\n outputs: [\n \"site\",\n ...plain,\n ...(entry === reviewed && review?.active ? ([\"json\"] as const) : []),\n ],\n });\n if (lines.summary !== \"\") logger.info(lines.summary);\n }\n // The schema's copy inside the Astro root, for content.config.ts to import.\n mkdirSync(entry.schemaDir, { recursive: true });\n copySchema(entry.siteRoot, entry.schemaDir);\n };\n // What the projects share, which no one project's build can see.\n const checkShared = () => {\n if (entries.length < 2) return;\n const current = entries.map((entry) => ({\n ...entry,\n project: readProject(entry.project.dir, { binary: options.binary, root }),\n }));\n fail(basePathClashes(current), \"two projects share a base path\");\n fail(pagesUnderOtherPaths(current), \"a page is under another project's base path\");\n fail(\n permalinkProblems(\n current,\n current.map((entry) => readPermalinks(entry.siteRoot)),\n ),\n \"the projects' permalinks can't share one route\",\n );\n };\n // One group for each project: a change rebuilds the project it's in.\n const byDir = new Map<string, Entry[]>();\n for (const entry of entries) {\n byDir.set(entry.project.dir, [...(byDir.get(entry.project.dir) ?? []), entry]);\n }\n groups = [...byDir.values()].map((group) => ({\n project: (group[0] as Entry).project,\n entries: group,\n rebuild: async () => {\n for (const entry of group) await buildEntry(entry);\n checkShared();\n },\n }));\n // The sources as the first build reads them: the watcher may report their edits late.\n if (command === \"dev\") {\n for (const group of groups)\n seen.prime([group.project.contentRoot, group.project.configPath]);\n }\n for (const entry of entries) await buildEntry(entry);\n checkShared();\n devCommand = command === \"dev\";\n if (devCommand) {\n // A tag of the head written twice, by Head.astro and the layout, is warned about.\n (globalThis as Record<symbol, unknown>)[HEAD_WARN] = (message: string) =>\n logger.warn(message);\n addMiddleware({\n entrypoint: new URL(\"./head-check.js\", import.meta.url),\n order: \"post\",\n });\n }\n\n // Each permalink's address, a page that forwards to its heading. One route serves every\n // list, at the path of the first that names anything.\n const lists = entries.flatMap((entry) => readPermalinks(entry.siteRoot) ?? []);\n const served = lists.find(hasPermalinks) ?? lists[0];\n if (served !== undefined) {\n injectRoute({\n pattern: permalinkPattern(served.path, astro.base),\n entrypoint: new URL(\"../src/Permalink.astro\", import.meta.url),\n prerender: true,\n });\n }\n // Each permalink's help, at `<name>.json` beside its address, and each older line's\n // under the line's base path. One route for each folder: the current line's, then each\n // older line's.\n for (const pattern of helpPatterns(\n entries.map((entry) => entry.siteRoot),\n astro.base,\n )) {\n injectRoute({\n pattern,\n entrypoint: new URL(\"./help-route.js\", import.meta.url),\n prerender: true,\n });\n }\n }\n\n const projectHome = (entry: Entry) =>\n homeRoute(entry.project.consumer.basePath, astro.trailingSlash);\n // Dev serving of `_ascribe/files/`, and of the plain output for agents; the build copies them in `astro:build:done`.\n updateConfig({\n ...supplied,\n ...(options.codeTitles === false\n ? {}\n : { markdown: { shikiConfig: { transformers: [codeTitles] } } }),\n vite: {\n // `Elements.astro` is Astro source, so Vite must compile it, not load it as a Node module.\n ssr: { noExternal: [\"@ascribed/astro\"] },\n plugins: [\n {\n name: \"@ascribed/astro:site\",\n resolveId: (id) => (id === SITE_MODULE ? `\\0${SITE_MODULE}` : undefined),\n load: (id) =>\n id === `\\0${SITE_MODULE}`\n ? `export const siteRoot = ${JSON.stringify(entries[0]?.siteRoot ?? \"\")};\\n` +\n `export const astroRoot = ${JSON.stringify(root)};\\n` +\n `export const home = ${JSON.stringify(homeRoute(astro.base, astro.trailingSlash))};\\n` +\n `export const base = ${JSON.stringify(astro.base)};\\n` +\n `export const collections = ${JSON.stringify(\n named ? Object.fromEntries(entries.map((e) => [e.name, e.siteRoot])) : {},\n )};\\n` +\n `export const permalinks = ${JSON.stringify(\n entries.map((e) => ({ siteRoot: e.siteRoot, home: projectHome(e) })),\n )};\\n` +\n `export const routes = ${JSON.stringify(\n entries.map((e) => ({\n siteRoot: e.siteRoot,\n basePath: e.project.consumer.basePath,\n trailingSlash: e.project.consumer.trailingSlash,\n })),\n )};`\n : undefined,\n },\n {\n name: \"@ascribed/astro:files\",\n configureServer(server) {\n for (const entry of entries) {\n const base = entry.project.consumer.basePath;\n server.middlewares.use(filesMiddleware(entry.siteRoot, base));\n // Each older line's, at its own base path; the lines are read as each request comes.\n server.middlewares.use((req, res, next) =>\n inOrder(\n olderLines(entry.siteRoot).map((line) =>\n filesMiddleware(line.siteRoot, line.base),\n ),\n req,\n res,\n next,\n ),\n );\n if (entry.agentsRoot !== undefined) {\n const siteRoot = entry.siteRoot;\n server.middlewares.use(\n agentsMiddleware(entry.agentsRoot, base, (pathname) =>\n headersFor(siteRoot, pathname),\n ),\n );\n // Each older line's outputs for agents, at its own base path.\n server.middlewares.use((req, res, next) =>\n inOrder(\n olderLines(siteRoot).map((line) =>\n agentsMiddleware(line.plainRoot, line.base, (pathname) =>\n headersFor(line.siteRoot, pathname),\n ),\n ),\n req,\n res,\n next,\n ),\n );\n }\n }\n // The sitemap and robots.txt, as the build would write them now.\n server.middlewares.use(\n rootFilesMiddleware(astro.base, () =>\n rootFiles(\n rootSources(\n devSitePages(astroRoutes, astro),\n devRouted(astroRoutes, astro),\n ),\n ),\n ),\n );\n },\n },\n ],\n },\n });\n },\n \"astro:routes:resolved\": ({ routes }) => {\n astroRoutes = routes;\n },\n \"astro:server:setup\": ({ server, refreshContent, logger, toolbar }) => {\n if (!devCommand || !routing) return;\n if (!refreshContent)\n throw new Error(\"@ascribed/astro: this Astro version does not support refreshContent.\");\n devHttps = Boolean(server.config.server.https);\n for (const group of groups) {\n group.builds = watchDev({\n server,\n seen,\n refreshContent: () => refreshContent({}),\n logger,\n project: group.project,\n astro: routing,\n builds: group.entries.map((entry) => entry.build),\n rebuild: group.rebuild,\n ...(binary === undefined ? {} : { binary: { binary, root: group.project.dir } }),\n onBuilt: () => {\n if (reviewed !== undefined && group.entries.includes(reviewed)) review?.rebuilt();\n },\n });\n }\n const group = groups.find((g) => reviewed !== undefined && g.entries.includes(reviewed));\n if (reviewed !== undefined && group?.builds !== undefined && binary !== undefined) {\n const entry = reviewed;\n const builds = group.builds;\n const run = { binary, configPath: entry.project.configPath, cwd: entry.project.dir };\n // A versioned build is reviewed in every line, the current one first, each line's\n // routes from its own JSON output.\n const lines = () =>\n linesOf(entry.siteRoot)?.map((line) => ({\n build: line.build,\n siteRoot: line.siteRoot,\n })) ?? [{ build: entry.build, siteRoot: entry.siteRoot }];\n const lineBuilds = () => lines().map((line) => line.build);\n review = new ReviewServer({\n channel: toolbar as ToolbarChannel,\n logger,\n build: entry.build,\n contentRoot: entry.project.contentRoot,\n channelProblem: channelProblem(server.config),\n diff: (base) => runDiff({ ...run, build: lineBuilds(), base }),\n prompt: (page, base) => runPrompt({ ...run, build: lineBuilds(), base, path: page }),\n projectDir: entry.project.dir,\n connect: async () => (await import(\"./review/github.js\")).connect(entry.project.dir),\n writeRoutes: () =>\n builds.inTurn(async () => {\n await runBuild({ ...run, build: lineBuilds(), outputs: [\"json\"] });\n }),\n readRoutes: async () => {\n const all = new Map<string, RoutedPage>();\n const versioned = lines().length > 1;\n for (const line of lines()) {\n const jsonRoot = path.join(path.dirname(line.siteRoot), \"json\");\n for (const [route, page] of await readRoutes(jsonRoot)) {\n if (!all.has(route))\n all.set(route, versioned ? { ...page, build: line.build } : page);\n }\n }\n return all;\n },\n });\n }\n },\n \"astro:server:start\": ({ address, logger }) => {\n review?.listening(address.address);\n if (!devCommand) return;\n // Each project's folder gets its own file, with its own address: its base path.\n for (const group of groups) {\n const entry = group.entries[0] as Entry;\n const dir = group.project.dir;\n try {\n writeDevFile(dir, {\n url: siteUrl(address, { https: devHttps, base: entry.project.consumer.basePath }),\n build: entry.build,\n pid: process.pid,\n });\n // Vite exits on a signal before `astro:server:done` runs.\n process.once(\"exit\", () => removeDevFile(dir, process.pid));\n } catch (error) {\n logger.warn(\n `couldn't write ${groups.length > 1 ? path.join(dir, \".ascribe\", \"dev.json\") : \".ascribe/dev.json\"}, which the editor's Open Site Preview reads: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n },\n \"astro:server:done\": () => {\n review?.dispose();\n review = undefined;\n if (devCommand) for (const group of groups) removeDevFile(group.project.dir, process.pid);\n },\n \"astro:build:done\": async ({ dir, pages, logger }) => {\n const base = routing?.base ?? \"/\";\n const trailingSlash = routing?.trailingSlash ?? \"ignore\";\n // Every page Astro built, by its route from the host's root. Astro lists\n // pages only, not endpoints such as a feed, so a dot in a page's last\n // segment, as in `/v1.2`, is still a page.\n const built = pages.map((page) =>\n routeAs(`${normalizeBase(base)}${page.pathname.replace(/^\\//, \"\")}`, trailingSlash),\n );\n const builtKeys = new Set(built.map(routeKey));\n const files = rootFiles(rootSources(built, (route) => builtKeys.has(routeKey(route))));\n fail(files.problems, \"the site's robots.txt\");\n for (const note of files.notes) logger[note.level](note.text);\n const out = fileURLToPath(dir);\n if (files.sitemap !== undefined) {\n await writeFile(path.join(out, \"sitemap.xml\"), files.sitemap);\n logger.info(`wrote ${sitemapRoute(base)}`);\n }\n if (files.robots !== undefined) {\n await writeFile(path.join(out, \"robots.txt\"), files.robots);\n logger.info(\"wrote /robots.txt\");\n }\n // The host's headers, from the projects that name its format.\n const ownHeaders = path.join(publicDir, \"_headers\");\n const headers = headersOutcome({\n rules: mergeRules(\n entries\n .filter((entry) => entry.project.consumer.headers === \"netlify\")\n .flatMap((entry) =>\n [entry.siteRoot, ...olderLines(entry.siteRoot).map((line) => line.siteRoot)].map(\n (siteRoot) => readHeaders(siteRoot) ?? [],\n ),\n ),\n ),\n base,\n own: existsSync(ownHeaders) ? readFileSync(ownHeaders, \"utf8\") : undefined,\n });\n if (headers.kind === \"fail\") throw new Error(`@ascribed/astro: ${headers.text}`);\n if (headers.kind === \"note\") logger.info(headers.text);\n if (headers.kind === \"write\") {\n await writeFile(path.join(out, \"_headers\"), headers.text);\n logger.info(\"wrote /_headers\");\n }\n // Entries of one project publish at one base path: the first entry's copies go last, so they win.\n for (const entry of [...entries].reverse()) {\n const folder = folderUnder(entry.project.consumer.basePath, base);\n const where = folder === \"\" ? \"\" : ` to ${folder}`;\n if (await copyPublishedFiles(entry.siteRoot, dir, folder))\n logger.info(`copied _ascribe/files/${where}`);\n for (const line of olderLines(entry.siteRoot)) {\n const lineFolder = folderUnder(line.base, base);\n if (await copyPublishedFiles(line.siteRoot, dir, lineFolder))\n logger.info(`copied ${line.build}'s _ascribe/files/ to ${lineFolder}`);\n }\n if (\n entry.agentsRoot !== undefined &&\n (await copyAgentFiles(entry.agentsRoot, dir, folder))\n )\n logger.info(`copied llms.txt and each page's Markdown${where}`);\n if (entry.agentsRoot !== undefined) {\n for (const line of olderLines(entry.siteRoot)) {\n const lineFolder = folderUnder(line.base, base);\n if (await copyAgentFiles(line.plainRoot, dir, lineFolder))\n logger.info(\n `copied ${line.build}'s llms.txt and each page's Markdown to ${lineFolder}`,\n );\n }\n }\n }\n },\n },\n };\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,SAAS;AACT,EAAE;AACF,wBAAwB;AACxB,2CAA2C;AAC3C,kCAAkC;AAClC,sEAAsE;AACtE,2EAA2E;AAC3E,kDAAkD;AAClD,QAAQ;AACR,EAAE;AACF,6BAA6B;AAC7B,sDAAsD;AACtD,iEAAiE;AACjE,iFAAiF;AACjF,mFAAmF;AACnF,6EAA6E;AAC7E,0FAA0F;AAC1F,EAAE;AACF,gBAAgB;AAChB,+FAA+F;AAC/F,gGAAgG;AAChG,yGAAyG;AACzG,qGAAqG;AACrG,EAAE;AACF,uEAAuE;AACvE,uDAAuD;AACvD,yDAAyD;AACzD,EAAE;AACF,2BAA2B;AAC3B,0GAA0G;AAC1G,EAAE;AACF,wFAAwF;AACxF,EAAE;AACF,cAAc;AACd,kBAAkB;AAClB,6DAA6D;AAC7D,0DAA0D;AAC1D,SAAS;AACT,sBAAsB;AACtB,OAAO;AACP,gDAAgD;AAChD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAkB,MAAM,UAAU,CAAC;AAC/D,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,OAAO,GACR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACvF,OAAO,EACL,YAAY,EACZ,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACpG,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,aAAa,GAEd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,OAAO,EACP,QAAQ,EACR,YAAY,GAEb,MAAM,cAAc,CAAC;AACtB,OAAO,uBAAuB,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAuB,MAAM,oBAAoB,CAAC;AACvF,OAAO,EACL,SAAS,EACT,YAAY,EACZ,SAAS,EACT,mBAAmB,GAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAGxD,iFAAiF;AACjF,MAAM,WAAW,GAAG,sBAAsB,CAAC;AA2D3C,2DAA2D;AAC3D,MAAM,IAAI,GACR,8QAA8Q,CAAC;AAmBjR,SAAS,IAAI,CAAC,QAAkB,EAAE,IAAY;IAC5C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,QAAQ,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACpG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,OAAuB;IACrD,+EAA+E;IAC/E,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC;IAC7C,IAAI,OAAO,GAAY,EAAE,CAAC;IAC1B,IAAI,MAAM,GAAe,EAAE,CAAC;IAC5B,IAAI,OAAiC,CAAC;IACtC,6DAA6D;IAC7D,MAAM,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;IAC7B,IAAI,MAA0B,CAAC;IAC/B,IAAI,QAA2B,CAAC;IAChC,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,MAAgC,CAAC;IACrC,gGAAgG;IAChG,IAAI,WAAW,GAA+B,EAAE,CAAC;IACjD,wEAAwE;IACxE,IAAI,SAAS,GAAG,EAAE,CAAC;IAEnB,+FAA+F;IAC/F,MAAM,WAAW,GAAG,CAClB,SAA4B,EAC5B,MAAkC,EACjB,EAAE;QACnB,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,GAAG,CAAC;QAClC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpE,oFAAoF;QACpF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;QAChD,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBAChC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;oBAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;iBAC5C,CAAC,CAAC;QACP,CAAC;QACD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO;YACL,QAAQ,EAAE;gBACR,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACxB,oFAAoF;gBACpF,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAC3B,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC/E;aACF;YACD,MAAM;YACN,SAAS;YACT,QAAQ,EAAE;gBACR,GAAG,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC3E,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACrD,GAAG,IAAI,KAAK;gBACZ,GAAG,IAAI,KAAK;gBACZ,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;aACpC;YACD,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;YAC5B,IAAI,EAAE,OAAO,EAAE,IAAI;YACnB,IAAI;YACJ,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;YACzD,UAAU,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;SAC5D,CAAC;IACJ,CAAC,CAAC;IACF,OAAO;QACL,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE;YACL,oBAAoB,EAAE,KAAK,EAAE,EAC3B,MAAM,EACN,OAAO,EACP,MAAM,EACN,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,aAAa,GACd,EAAE,EAAE;gBACH,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACxC,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC5C,MAAM,IAAI,GAAmB,OAAO,CAAC,QAAQ,IAAI;oBAC/C;wBACE,IAAI,EAAE,EAAE;wBACR,KAAK,EAAE,OAAO,CAAC,KAAK;wBACpB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;qBACvE;iBACF,CAAC;gBACF,IAAI,KAAK;oBAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,gCAAgC,CAAC,CAAC;gBACtE,MAAM,OAAO,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,gBAAgB,EAAE,CAAC,CAAC;gBAC/E,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;oBAC3B,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC,EAAE;wBACpE,MAAM,EAAE,OAAO,CAAC,MAAM;wBACtB,IAAI;qBACL,CAAC,CAAC;oBACH,OAAO;wBACL,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,KAAK,EAAE,KAAK,CAAC,KAAK;wBAClB,OAAO;wBACP,uFAAuF;wBACvF,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;wBACvC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;wBAChF,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO;qBAC5D,CAAC;gBACJ,CAAC,CAAC,CAAC;gBAEH,4FAA4F;gBAC5F,qFAAqF;gBACrF,0EAA0E;gBAC1E,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,CAChD,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,EACrC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,EAC7E,CAAC,KAAK,CACP,CAAC;gBACF,mFAAmF;gBACnF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;oBAC5B,IAAI,CACF,kBAAkB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,EACxC,KAAK;wBACH,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,4CAA4C;wBAC5D,CAAC,CAAC,wCAAwC,CAC7C,CAAC;gBACJ,CAAC;gBACD,gGAAgG;gBAChG,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAC3E,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,wCAAwC,CAAC,CAAC;gBAChE,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,gCAAgC,CAAC,CAAC;gBAEjE,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1F,MAAM,IAAI,KAAK,CACb,sCAAsC,OAAO,CAAC,MAAM,gDAAgD,CACrG,CAAC;gBACJ,CAAC;gBACD,IAAI,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;oBACvC,QAAQ;wBACN,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;4BAChC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC;4BAChD,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACnB,CAAC;gBAED,wEAAwE;gBACxE,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAC5C,MAAM,WAAW,GAAG,SAAS,CAAC,OAG7B,CAAC;gBACF,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,IAAI,WAAW,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;oBAC1E,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,CAAC;gBAC3D,CAAC;qBAAM,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,IAAI,WAAW,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;oBACnF,WAAW,CAAC,aAAa,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;gBAC1D,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,KAAK,CACb,4CAA4C,SAAS,CAAC,IAAI,yCAAyC;wBACjG,2FAA2F,CAC9F,CAAC;gBACJ,CAAC;gBAED,OAAO,GAAG,KAAK,CAAC;gBAEhB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,gBAAgB,CAAC;wBACf,EAAE,EAAE,MAAM;wBACV,IAAI,EAAE,gBAAgB;wBACtB,IAAI,EAAE,IAAI;wBACV,UAAU,EAAE,IAAI,GAAG,CAAC,kBAAkB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC;qBACzD,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;oBAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC3D,MAAM,GAAG,KAAK,CAAC;oBACf,MAAM,UAAU,GAAG,KAAK,EAAE,KAAY,EAAE,EAAE;wBACxC,MAAM,CAAC,IAAI,CACT,WAAW,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,eAAe,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/F,CAAC;wBACF,MAAM,GAAG,GAAG;4BACV,MAAM,EAAE,KAAK;4BACb,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU;4BACpC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG;4BACtB,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,KAAK,QAAQ,CAAC;yBAClE,CAAC;wBACF,0DAA0D;wBAC1D,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,CAAC,OAAO,CAAW,CAAC;wBACzE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;4BAC5B,GAAG,GAAG;4BACN,KAAK,EAAE,KAAK,CAAC,KAAK;4BAClB,OAAO,EAAE;gCACP,MAAM;gCACN,GAAG,KAAK;gCACR,wEAAwE;gCACxE,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,MAAM,CAAW,CAAC,CAAC,CAAC,EAAE,CAAC;6BACrE;yBACF,CAAC,CAAC;wBACH,IAAI,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC;4BAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;6BAChE,IAAI,MAAM,CAAC,WAAW,KAAK,EAAE;4BAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;wBACpE,IAAI,MAAM,CAAC,OAAO,KAAK,EAAE;4BAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;wBACvD,qFAAqF;wBACrF,sFAAsF;wBACtF,wFAAwF;wBACxF,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;wBACnE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACrB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC;gCAC3B,GAAG,GAAG;gCACN,KAAK,EAAE,KAAK;gCACZ,OAAO,EAAE;oCACP,MAAM;oCACN,GAAG,KAAK;oCACR,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,MAAM,CAAW,CAAC,CAAC,CAAC,EAAE,CAAC;iCACrE;6BACF,CAAC,CAAC;4BACH,IAAI,KAAK,CAAC,OAAO,KAAK,EAAE;gCAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;wBACvD,CAAC;wBACD,4EAA4E;wBAC5E,SAAS,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;wBAChD,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;oBAC9C,CAAC,CAAC;oBACF,iEAAiE;oBACjE,MAAM,WAAW,GAAG,GAAG,EAAE;wBACvB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;4BAAE,OAAO;wBAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;4BACtC,GAAG,KAAK;4BACR,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;yBAC1E,CAAC,CAAC,CAAC;wBACJ,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,gCAAgC,CAAC,CAAC;wBACjE,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,6CAA6C,CAAC,CAAC;wBACnF,IAAI,CACF,iBAAiB,CACf,OAAO,EACP,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CACvD,EACD,gDAAgD,CACjD,CAAC;oBACJ,CAAC,CAAC;oBACF,qEAAqE;oBACrE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmB,CAAC;oBACzC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;wBAC5B,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;oBACjF,CAAC;oBACD,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;wBAC3C,OAAO,EAAG,KAAK,CAAC,CAAC,CAAW,CAAC,OAAO;wBACpC,OAAO,EAAE,KAAK;wBACd,OAAO,EAAE,KAAK,IAAI,EAAE;4BAClB,KAAK,MAAM,KAAK,IAAI,KAAK;gCAAE,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;4BACnD,WAAW,EAAE,CAAC;wBAChB,CAAC;qBACF,CAAC,CAAC,CAAC;oBACJ,sFAAsF;oBACtF,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;wBACtB,KAAK,MAAM,KAAK,IAAI,MAAM;4BACxB,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;oBACtE,CAAC;oBACD,KAAK,MAAM,KAAK,IAAI,OAAO;wBAAE,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;oBACrD,WAAW,EAAE,CAAC;oBACd,UAAU,GAAG,OAAO,KAAK,KAAK,CAAC;oBAC/B,IAAI,UAAU,EAAE,CAAC;wBACf,kFAAkF;wBACjF,UAAsC,CAAC,SAAS,CAAC,GAAG,CAAC,OAAe,EAAE,EAAE,CACvE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;wBACvB,aAAa,CAAC;4BACZ,UAAU,EAAE,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC;4BACvD,KAAK,EAAE,MAAM;yBACd,CAAC,CAAC;oBACL,CAAC;oBAED,wFAAwF;oBACxF,sDAAsD;oBACtD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;oBAC/E,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;oBACrD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;wBACzB,WAAW,CAAC;4BACV,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;4BAClD,UAAU,EAAE,IAAI,GAAG,CAAC,wBAAwB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC;4BAC9D,SAAS,EAAE,IAAI;yBAChB,CAAC,CAAC;oBACL,CAAC;oBACD,wFAAwF;oBACxF,8DAA8D;oBAC9D,KAAK,MAAM,OAAO,IAAI,YAAY,CAChC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EACtC,KAAK,CAAC,IAAI,CACX,EAAE,CAAC;wBACF,WAAW,CAAC;4BACV,OAAO;4BACP,UAAU,EAAE,IAAI,GAAG,CAAC,mBAAmB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC;4BACzD,SAAS,EAAE,IAAI;yBAChB,CAAC,CAAC;oBACL,CAAC;oBACD,oFAAoF;oBACpF,uFAAuF;oBACvF,gBAAgB;oBAChB,KAAK,MAAM,OAAO,IAAI,YAAY,CAChC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EACtC,KAAK,CAAC,IAAI,CACX,EAAE,CAAC;wBACF,WAAW,CAAC;4BACV,OAAO;4BACP,UAAU,EAAE,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC;4BACvD,SAAS,EAAE,IAAI;yBAChB,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBAED,MAAM,WAAW,GAAG,CAAC,KAAY,EAAE,EAAE,CACnC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;gBAClE,qHAAqH;gBACrH,YAAY,CAAC;oBACX,GAAG,QAAQ;oBACX,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;oBAChF,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,KAAK;wBAC9B,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,YAAY,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC;oBAClE,IAAI,EAAE;wBACJ,2FAA2F;wBAC3F,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,iBAAiB,CAAC,EAAE;wBACxC,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,sBAAsB;gCAC5B,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;gCACxE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CACX,EAAE,KAAK,KAAK,WAAW,EAAE;oCACvB,CAAC,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,EAAE,CAAC,KAAK;wCAC1E,4BAA4B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK;wCACrD,uBAAuB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,KAAK;wCACtF,uBAAuB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK;wCACtD,8BAA8B,IAAI,CAAC,SAAS,CAC1C,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAC1E,KAAK;wCACN,6BAA6B,IAAI,CAAC,SAAS,CACzC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CACrE,KAAK;wCACN,yBAAyB,IAAI,CAAC,SAAS,CACrC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;4CAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;4CACpB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ;4CACrC,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa;yCAChD,CAAC,CAAC,CACJ,GAAG;oCACN,CAAC,CAAC,SAAS;6BAChB;4BACD;gCACE,IAAI,EAAE,uBAAuB;gCAC7B,eAAe,CAAC,MAAM;oCACpB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;wCAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;wCAC7C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;wCAC9D,qFAAqF;wCACrF,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CACxC,OAAO,CACL,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACtC,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAC1C,EACD,GAAG,EACH,GAAG,EACH,IAAI,CACL,CACF,CAAC;wCACF,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;4CACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;4CAChC,MAAM,CAAC,WAAW,CAAC,GAAG,CACpB,gBAAgB,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,EAAE,CACpD,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAC/B,CACF,CAAC;4CACF,8DAA8D;4CAC9D,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CACxC,OAAO,CACL,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAChC,gBAAgB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,EAAE,CACvD,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CACpC,CACF,EACD,GAAG,EACH,GAAG,EACH,IAAI,CACL,CACF,CAAC;wCACJ,CAAC;oCACH,CAAC;oCACD,iEAAiE;oCACjE,MAAM,CAAC,WAAW,CAAC,GAAG,CACpB,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CACnC,SAAS,CACP,WAAW,CACT,YAAY,CAAC,WAAW,EAAE,KAAK,CAAC,EAChC,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAC9B,CACF,CACF,CACF,CAAC;gCACJ,CAAC;6BACF;yBACF;qBACF;iBACF,CAAC,CAAC;YACL,CAAC;YACD,uBAAuB,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;gBACtC,WAAW,GAAG,MAAM,CAAC;YACvB,CAAC;YACD,oBAAoB,EAAE,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;gBACpE,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO;oBAAE,OAAO;gBACpC,IAAI,CAAC,cAAc;oBACjB,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;gBAC1F,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC/C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBAC3B,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC;wBACtB,MAAM;wBACN,IAAI;wBACJ,cAAc,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC;wBACxC,MAAM;wBACN,OAAO,EAAE,KAAK,CAAC,OAAO;wBACtB,KAAK,EAAE,OAAO;wBACd,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC;wBACjD,OAAO,EAAE,KAAK,CAAC,OAAO;wBACtB,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;wBAChF,OAAO,EAAE,GAAG,EAAE;4BACZ,IAAI,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gCAAE,MAAM,EAAE,OAAO,EAAE,CAAC;wBACpF,CAAC;qBACF,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACzF,IAAI,QAAQ,KAAK,SAAS,IAAI,KAAK,EAAE,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBAClF,MAAM,KAAK,GAAG,QAAQ,CAAC;oBACvB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;oBAC5B,MAAM,GAAG,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;oBACrF,kFAAkF;oBAClF,mCAAmC;oBACnC,MAAM,KAAK,GAAG,GAAG,EAAE,CACjB,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;wBACtC,KAAK,EAAE,IAAI,CAAC,KAAK;wBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;qBACxB,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC5D,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBAC3D,MAAM,GAAG,IAAI,YAAY,CAAC;wBACxB,OAAO,EAAE,OAAyB;wBAClC,MAAM;wBACN,KAAK,EAAE,KAAK,CAAC,KAAK;wBAClB,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW;wBACtC,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC;wBAC7C,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC;wBAC9D,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;wBACpF,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG;wBAC7B,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;wBACpF,WAAW,EAAE,GAAG,EAAE,CAChB,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;4BACvB,MAAM,QAAQ,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;wBACrE,CAAC,CAAC;wBACJ,UAAU,EAAE,KAAK,IAAI,EAAE;4BACrB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAsB,CAAC;4BAC1C,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;4BACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;gCAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;gCAChE,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;oCACvD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC;wCACjB,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gCACtE,CAAC;4BACH,CAAC;4BACD,OAAO,GAAG,CAAC;wBACb,CAAC;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YACD,oBAAoB,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC5C,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACnC,IAAI,CAAC,UAAU;oBAAE,OAAO;gBACxB,gFAAgF;gBAChF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAU,CAAC;oBACxC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;oBAC9B,IAAI,CAAC;wBACH,YAAY,CAAC,GAAG,EAAE;4BAChB,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;4BACjF,KAAK,EAAE,KAAK,CAAC,KAAK;4BAClB,GAAG,EAAE,OAAO,CAAC,GAAG;yBACjB,CAAC,CAAC;wBACH,0DAA0D;wBAC1D,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC9D,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,MAAM,CAAC,IAAI,CACT,kBAAkB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,mBAAmB,iDAAiD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC5M,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YACD,mBAAmB,EAAE,GAAG,EAAE;gBACxB,MAAM,EAAE,OAAO,EAAE,CAAC;gBAClB,MAAM,GAAG,SAAS,CAAC;gBACnB,IAAI,UAAU;oBAAE,KAAK,MAAM,KAAK,IAAI,MAAM;wBAAE,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;YAC5F,CAAC;YACD,kBAAkB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;gBACnD,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,GAAG,CAAC;gBAClC,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,QAAQ,CAAC;gBACzD,yEAAyE;gBACzE,sEAAsE;gBACtE,2CAA2C;gBAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/B,OAAO,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,CACpF,CAAC;gBACF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC/C,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvF,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC;gBAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK;oBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC9D,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;gBAC/B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;oBAChC,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;oBAC9D,MAAM,CAAC,IAAI,CAAC,SAAS,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC7C,CAAC;gBACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;oBAC/B,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC5D,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;gBACnC,CAAC;gBACD,kEAAkE;gBAClE,gCAAgC;gBAChC,MAAM,SAAS,GAAG,OAAO;qBACtB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC;qBAC5D,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;oBAClB,KAAK,CAAC,QAAQ;oBACd,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;iBAC3D,CAAC,CAAC;gBACL,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;gBACpD,MAAM,OAAO,GAAG,cAAc,CAAC;oBAC7B,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;oBAC/E,IAAI;oBACJ,GAAG,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;iBAC3E,CAAC,CAAC;gBACH,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;gBACjF,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;oBAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACvD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC7B,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;oBAC1D,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;gBACjC,CAAC;gBACD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;gBACxD,MAAM,SAAS,GAAG,gBAAgB,CAAC;oBACjC,SAAS,EAAE,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;oBAC3D,IAAI;oBACJ,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;oBACpD,GAAG,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;iBAC/E,CAAC,CAAC;gBACH,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;gBACrF,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM;oBAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBAC3D,IAAI,SAAS,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC/B,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;oBAC9D,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;gBACnC,CAAC;gBACD,kGAAkG;gBAClG,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;oBAC3C,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;oBAClE,MAAM,KAAK,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAC;oBACnD,IAAI,MAAM,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC;wBACvD,MAAM,CAAC,IAAI,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;oBAChD,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC9C,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;wBAChD,IAAI,MAAM,kBAAkB,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,UAAU,CAAC;4BAC1D,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,yBAAyB,UAAU,EAAE,CAAC,CAAC;oBAC3E,CAAC;oBACD,IACE,KAAK,CAAC,UAAU,KAAK,SAAS;wBAC9B,CAAC,MAAM,cAAc,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;wBAErD,MAAM,CAAC,IAAI,CAAC,2CAA2C,KAAK,EAAE,CAAC,CAAC;oBAClE,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;wBACnC,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;4BAC9C,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;4BAChD,IAAI,MAAM,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,UAAU,CAAC;gCACvD,MAAM,CAAC,IAAI,CACT,UAAU,IAAI,CAAC,KAAK,2CAA2C,UAAU,EAAE,CAC5E,CAAC;wBACN,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;SACF;KACF,CAAC;AACJ,CAAC","sourcesContent":["// @ascribed/astro: the Astro integration for Ascribe. Written against Astro\n// 7.3.5.\n//\n// // astro.config.mjs\n// import ascribe from \"@ascribed/astro\";\n// export default defineConfig({\n// // `site`, `base`, and `trailingSlash` come from ascribe.toml's\n// // [consumer] when they're left out here; set here, they must agree.\n// integrations: [ascribe({ build: \"site\" })],\n// });\n//\n// // src/content.config.ts\n// import { defineCollection } from \"astro:content\";\n// import { ascribeCollection } from \"@ascribed/astro/content\";\n// // The generated schema. With the project outside the Astro root, import the\n// // copy inside it instead: \"../.astro/integrations/_ascribed_astro/schema.ts\".\n// import { schema } from \"../.ascribe/build/site/site/_ascribe/schema.ts\";\n// export const collections = { docs: defineCollection(ascribeCollection({ schema })) };\n//\n// // a layout\n// import Elements from \"@ascribed/astro/Elements.astro\"; // <Elements /> in <head>\n// import Availability from \"@ascribed/astro/Availability.astro\"; // the page's availability\n// import Head from \"@ascribed/astro/Head.astro\"; // <Head entry={entry} /> in <head>\n// import Versions from \"@ascribed/astro/Versions.astro\"; // the lines that have the page\n//\n// // the sidebar: the project's navigation, joined to the collection\n// import { sidebar } from \"@ascribed/astro/content\";\n// const groups = sidebar(await getCollection(\"docs\"));\n//\n// // src/pages/404.astro\n// import MissingPermalink from \"@ascribed/astro/MissingPermalink.astro\"; // a permalink that isn't here\n//\n// Several projects in one site, each entry named, and each collection naming its entry:\n//\n// ascribe({\n// projects: [\n// { name: \"docs\", project: \"../docs\", build: \"site\" },\n// { name: \"home\", project: \"home\", build: \"site\" },\n// ],\n// review: \"docs\",\n// })\n// ascribeCollection({ schema, name: \"docs\" })\nimport { existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AstroIntegration, IntegrationResolvedRoute } from \"astro\";\nimport { findBinary } from \"./binary.js\";\nimport { codeTitles } from \"./code-titles.js\";\nimport { SeenFiles, watchDev, type DevBuilds } from \"./dev.js\";\nimport {\n agentsMiddleware,\n copyAgentFiles,\n copyPublishedFiles,\n filesMiddleware,\n folderUnder,\n inOrder,\n} from \"./files.js\";\nimport { WARN as HEAD_WARN } from \"./head-check.js\";\nimport { headersFor, headersOutcome, mergeRules, readHostHeaders } from \"./headers.js\";\nimport {\n gonePatterns,\n goneRoutes,\n mergeRedirects,\n readHostRedirects,\n redirectsOutcome,\n} from \"./redirects.js\";\nimport { helpPatterns } from \"./help.js\";\nimport { forwardingRoutes, hasPermalinks, permalinkPattern, readPermalinks } from \"./permalinks.js\";\nimport {\n consumerMismatches,\n homeRoute,\n normalizeBase,\n readProject,\n supplyBuildFormat,\n supplyRouting,\n type AstroRouting,\n} from \"./project.js\";\nimport {\n basePathClashes,\n nameProblems,\n pagesUnderOtherPaths,\n permalinkProblems,\n type ProjectEntry,\n type ResolvedEntry,\n} from \"./projects.js\";\nimport {\n pageList,\n readRecords,\n routeAs,\n routeKey,\n sitemapRoute,\n type ProjectRobots,\n} from \"./records.js\";\nimport rehypeAscribeAttributes from \"./rehype.js\";\nimport { removeDevFile, siteUrl, writeDevFile } from \"./review/devfile.js\";\nimport { runDiff, runPrompt } from \"./review/diff.js\";\nimport { APP_ID } from \"./review/protocol.js\";\nimport { readRoutes, type RoutedPage } from \"./review/routes.js\";\nimport { channelProblem, ReviewServer, type ToolbarChannel } from \"./review/server.js\";\nimport {\n devRouted,\n devSitePages,\n rootFiles,\n rootFilesMiddleware,\n type RootFileSources,\n} from \"./root-files.js\";\nimport { anchorsFor, hasWarnings, reviewFor, runBuild } from \"./run.js\";\nimport { satteriAscribeAttributes } from \"./satteri.js\";\nimport { copySchema } from \"./schema.js\";\nimport { linesOf, olderLines } from \"./versions.js\";\n\nexport { default as rehypeAscribeAttributes } from \"./rehype.js\";\nexport { satteriAscribeAttributes } from \"./satteri.js\";\nexport type { ProjectEntry } from \"./projects.js\";\n\n/** The virtual module `content.ts` reads the resolved site output roots from. */\nconst SITE_MODULE = \"virtual:ascribe/site\";\n\n/** Options every form of the integration takes. */\ninterface SharedOptions {\n /**\n * The `ascribe` binary, relative to the Astro root. Default: `ASCRIBE_BIN`, then\n * the platform binary installed with `@ascribed/cli`.\n */\n binary?: string;\n /**\n * Mark each block of the site output with the source lines it came from\n * (source anchors), for review: `\"dev\"` in `astro dev` only, `true` always.\n * Default: `false`, though `review` has them in `astro dev`.\n */\n anchors?: boolean | \"dev\";\n /**\n * Review in the site preview: an **Ascribe review** app in `astro dev`'s\n * toolbar, which marks the page's changes and shows the pull request's\n * comments, with source anchors in `astro dev`. Default `true`; `false`\n * removes the app. `astro build` never has it. With several projects, it\n * reviews one: the entry a name names, or the first for `true`.\n */\n review?: boolean | string;\n /**\n * Show a code block's title (`title=\"…\"` in its info string, as `@snippet`\n * writes it), and what a tested example was tested with (`tested=\"…\"`),\n * above it, with a Shiki transformer that puts the block in a\n * `<figure class=\"code-title\">` with a `<figcaption>`. Default `true`.\n */\n codeTitles?: boolean;\n /**\n * The site's one sitemap, at `<base>sitemap.xml`, of every listed page of\n * every project and every page of the site's own. `exclude` lists routes,\n * from the host's root, of the site's own pages to leave out of it, such\n * as one that carries `noindex`.\n */\n sitemap?: { exclude?: string[] };\n}\n\n/** Options of the integration with one project. */\nexport interface OneProjectOptions extends SharedOptions {\n /** The build whose site output is the collection (a name in `ascribe.toml`). */\n build: string;\n /** The directory holding `ascribe.toml`, relative to the Astro root. Default: the root. */\n project?: string;\n projects?: never;\n}\n\n/** Options of the integration with several projects, each entry named. */\nexport interface ProjectsOptions extends SharedOptions {\n /** The projects, each a named build of a project; two entries may share a project. */\n projects: ProjectEntry[];\n build?: never;\n project?: never;\n}\n\n/** Options of the integration. */\nexport type AscribeOptions = OneProjectOptions | ProjectsOptions;\n\n/** The toolbar app's icon: a speech bubble over a page. */\nconst ICON =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 3h8l4 4v6\"/><path d=\"M14 3v4h4\"/><path d=\"M6 3v15\"/><path d=\"M11 14h10v6h-6l-3 2v-2h-1z\"/></svg>';\n\n/** An entry as the hooks use it. */\ninterface Entry extends ResolvedEntry {\n siteRoot: string;\n /** The plain output's root, when the project publishes it for agents. */\n agentsRoot: string | undefined;\n /** Where the generated schema's copy goes, inside the Astro root. */\n schemaDir: string;\n}\n\n/** A project with the entries that build it, which a dev watcher follows together. */\ninterface DevGroup {\n project: ResolvedEntry[\"project\"];\n entries: Entry[];\n rebuild: () => Promise<void>;\n builds?: DevBuilds;\n}\n\nfunction fail(problems: string[], what: string): void {\n if (problems.length > 0) throw new Error(`@ascribed/astro: ${what}:\\n- ${problems.join(\"\\n- \")}`);\n}\n\n/**\n * Runs `ascribe build` for each project, checks the site's routing, adds the markdown plugin, adds a\n * page for each permalink's address and an answer for each line's help entries, and serves published files, and, with `[consumer] agents = true`,\n * `llms.txt` and each page's Markdown, each at its project's base path; and writes the site's one\n * sitemap and `robots.txt` from every project's page records, and, with `[consumer] host =\n * \"netlify\"`, its `_headers` and `_redirects` from the host's files each build writes.\n */\nexport default function ascribe(options: AscribeOptions): AstroIntegration {\n /** With `projects`; otherwise the one entry has no name a collection gives. */\n const named = options.projects !== undefined;\n let entries: Entry[] = [];\n let groups: DevGroup[] = [];\n let routing: AstroRouting | undefined;\n /** The dev server's sources as its first build read them. */\n const seen = new SeenFiles();\n let binary: string | undefined;\n let reviewed: Entry | undefined;\n let devCommand = false;\n let devHttps = false;\n let review: ReviewServer | undefined;\n /** The site's routes, as Astro resolved them, for what `astro dev` serves of the site's own. */\n let astroRoutes: IntegrationResolvedRoute[] = [];\n /** The site's `public/` folder, where its own `robots.txt` would be. */\n let publicDir = \"\";\n\n /** What the sitemap and `robots.txt` are drawn from, given the site's pages and its routes. */\n const rootSources = (\n sitePages: readonly string[],\n routed: (route: string) => boolean,\n ): RootFileSources => {\n const base = routing?.base ?? \"/\";\n const records = entries.map((entry) => readRecords(entry.siteRoot));\n // Entries of one project share its `ascribe.toml`, and so what it says to crawlers.\n const robots = new Map<string, ProjectRobots>();\n for (const [i, entry] of entries.entries()) {\n if (!robots.has(entry.project.dir))\n robots.set(entry.project.dir, {\n name: entry.name,\n robots: records[i]?.robots ?? { lines: [] },\n });\n }\n const root = normalizeBase(base);\n return {\n projects: [\n ...records.map(pageList),\n // An older line's pages are the site's too: those the current line has point at it.\n ...entries.flatMap((entry) =>\n olderLines(entry.siteRoot).map((line) => pageList(readRecords(line.siteRoot))),\n ),\n ],\n routed,\n sitePages,\n excluded: [\n ...forwardingRoutes(entries.map((entry) => readPermalinks(entry.siteRoot))),\n ...goneRoutes(entries.map((entry) => entry.siteRoot)),\n `${root}404`,\n `${root}500`,\n ...(options.sitemap?.exclude ?? []),\n ],\n robots: [...robots.values()],\n site: routing?.site,\n base,\n ownRobots: existsSync(path.join(publicDir, \"robots.txt\")),\n ownSitemap: existsSync(path.join(publicDir, \"sitemap.xml\")),\n };\n };\n return {\n name: \"@ascribed/astro\",\n hooks: {\n \"astro:config:setup\": async ({\n config,\n command,\n logger,\n updateConfig,\n addDevToolbarApp,\n createCodegenDir,\n injectRoute,\n addMiddleware,\n }) => {\n const root = fileURLToPath(config.root);\n publicDir = fileURLToPath(config.publicDir);\n const list: ProjectEntry[] = options.projects ?? [\n {\n name: \"\",\n build: options.build,\n ...(options.project === undefined ? {} : { project: options.project }),\n },\n ];\n if (named) fail(nameProblems(list), \"the `projects` option is wrong\");\n const codegen = command === \"preview\" ? \"\" : fileURLToPath(createCodegenDir());\n entries = list.map((entry) => {\n const project = readProject(path.resolve(root, entry.project ?? \".\"), {\n binary: options.binary,\n root,\n });\n return {\n name: entry.name,\n build: entry.build,\n project,\n // An unknown build is `ascribe build`'s to report: it knows the implicit `site` build.\n siteRoot: project.siteRoot(entry.build),\n agentsRoot: project.consumer.agents ? project.plainRoot(entry.build) : undefined,\n schemaDir: named ? path.join(codegen, entry.name) : codegen,\n };\n });\n\n // What astro.config leaves unset comes from [consumer]: `site` and `trailingSlash` from the\n // first project that has them, and with one project, `base` from its base path. With\n // `projects`, `base` stays the site's, the root every base path is under.\n const { routing: astro, supplied } = supplyRouting(\n entries.map((entry) => entry.project),\n { base: config.base, trailingSlash: config.trailingSlash, site: config.site },\n !named,\n );\n // `ascribe.toml`'s routing must be Astro's, or every link Ascribe writes is wrong.\n for (const entry of entries) {\n fail(\n consumerMismatches(entry.project, astro),\n named\n ? `\"${entry.name}\"'s ascribe.toml and astro.config disagree`\n : \"ascribe.toml and astro.config disagree\",\n );\n }\n // Each page's file must be served at the route Ascribe publishes for it, not behind a redirect.\n const format = supplyBuildFormat(astro.trailingSlash, config.build.format);\n fail(format.problems, \"ascribe.toml and astro.config disagree\");\n fail(basePathClashes(entries), \"two projects share a base path\");\n\n if (typeof options.review === \"string\" && !entries.some((e) => e.name === options.review)) {\n throw new Error(\n `@ascribed/astro: \\`review\\` names \"${options.review}\", and no entry of \\`projects\\` has that name.`,\n );\n }\n if (reviewFor(options.review, command)) {\n reviewed =\n typeof options.review === \"string\"\n ? entries.find((e) => e.name === options.review)\n : entries[0];\n }\n\n // Where a markdown plugin goes depends on the processor Astro 7.3 runs.\n const processor = config.markdown.processor;\n const pluginLists = processor.options as {\n rehypePlugins?: unknown[];\n hastPlugins?: unknown[];\n };\n if (processor.name === \"satteri\" && pluginLists.hastPlugins !== undefined) {\n pluginLists.hastPlugins.push(satteriAscribeAttributes());\n } else if (processor.name === \"unified\" && pluginLists.rehypePlugins !== undefined) {\n pluginLists.rehypePlugins.push(rehypeAscribeAttributes);\n } else {\n throw new Error(\n `@ascribed/astro: the markdown processor \"${processor.name}\" is not one it can add its plugin to. ` +\n \"Use Astro's default (satteri) or unified(), or add `rehypeAscribeAttributes` to your own.\",\n );\n }\n\n routing = astro;\n\n if (reviewed !== undefined) {\n addDevToolbarApp({\n id: APP_ID,\n name: \"Ascribe review\",\n icon: ICON,\n entrypoint: new URL(\"./toolbar/app.js\", import.meta.url),\n });\n }\n\n if (command !== \"preview\") {\n const found = findBinary({ binary: options.binary, root });\n binary = found;\n const buildEntry = async (entry: Entry) => {\n logger.info(\n `running ${path.basename(found)} build for \"${entry.build}\"${named ? ` (${entry.name})` : \"\"}`,\n );\n const run = {\n binary: found,\n configPath: entry.project.configPath,\n cwd: entry.project.dir,\n anchors: anchorsFor(options.anchors, command, entry === reviewed),\n };\n // `llms.txt` and each page's Markdown, which agents read.\n const plain = entry.agentsRoot === undefined ? [] : ([\"plain\"] as const);\n const result = await runBuild({\n ...run,\n build: entry.build,\n outputs: [\n \"site\",\n ...plain,\n // While review is on, the JSON output says which page is at each route.\n ...(entry === reviewed && review?.active ? ([\"json\"] as const) : []),\n ],\n });\n if (hasWarnings(result.diagnostics)) logger.warn(result.diagnostics);\n else if (result.diagnostics !== \"\") logger.info(result.diagnostics);\n if (result.summary !== \"\") logger.info(result.summary);\n // A versioned build's other lines, which the build's `versions.json` names, once the\n // current line's build has written it. An error in one fails this run as it would the\n // first; `ascribe check` reports every line's warnings, so only what was built is said.\n const older = olderLines(entry.siteRoot).map((line) => line.build);\n if (older.length > 0) {\n const lines = await runBuild({\n ...run,\n build: older,\n outputs: [\n \"site\",\n ...plain,\n ...(entry === reviewed && review?.active ? ([\"json\"] as const) : []),\n ],\n });\n if (lines.summary !== \"\") logger.info(lines.summary);\n }\n // The schema's copy inside the Astro root, for content.config.ts to import.\n mkdirSync(entry.schemaDir, { recursive: true });\n copySchema(entry.siteRoot, entry.schemaDir);\n };\n // What the projects share, which no one project's build can see.\n const checkShared = () => {\n if (entries.length < 2) return;\n const current = entries.map((entry) => ({\n ...entry,\n project: readProject(entry.project.dir, { binary: options.binary, root }),\n }));\n fail(basePathClashes(current), \"two projects share a base path\");\n fail(pagesUnderOtherPaths(current), \"a page is under another project's base path\");\n fail(\n permalinkProblems(\n current,\n current.map((entry) => readPermalinks(entry.siteRoot)),\n ),\n \"the projects' permalinks can't share one route\",\n );\n };\n // One group for each project: a change rebuilds the project it's in.\n const byDir = new Map<string, Entry[]>();\n for (const entry of entries) {\n byDir.set(entry.project.dir, [...(byDir.get(entry.project.dir) ?? []), entry]);\n }\n groups = [...byDir.values()].map((group) => ({\n project: (group[0] as Entry).project,\n entries: group,\n rebuild: async () => {\n for (const entry of group) await buildEntry(entry);\n checkShared();\n },\n }));\n // The sources as the first build reads them: the watcher may report their edits late.\n if (command === \"dev\") {\n for (const group of groups)\n seen.prime([group.project.contentRoot, group.project.configPath]);\n }\n for (const entry of entries) await buildEntry(entry);\n checkShared();\n devCommand = command === \"dev\";\n if (devCommand) {\n // A tag of the head written twice, by Head.astro and the layout, is warned about.\n (globalThis as Record<symbol, unknown>)[HEAD_WARN] = (message: string) =>\n logger.warn(message);\n addMiddleware({\n entrypoint: new URL(\"./head-check.js\", import.meta.url),\n order: \"post\",\n });\n }\n\n // Each permalink's address, a page that forwards to its heading. One route serves every\n // list, at the path of the first that names anything.\n const lists = entries.flatMap((entry) => readPermalinks(entry.siteRoot) ?? []);\n const served = lists.find(hasPermalinks) ?? lists[0];\n if (served !== undefined) {\n injectRoute({\n pattern: permalinkPattern(served.path, astro.base),\n entrypoint: new URL(\"../src/Permalink.astro\", import.meta.url),\n prerender: true,\n });\n }\n // Each retired address's page, which the host's `_redirects` serves at the address with\n // a 404. One route for each folder: each build's base path's.\n for (const pattern of gonePatterns(\n entries.map((entry) => entry.siteRoot),\n astro.base,\n )) {\n injectRoute({\n pattern,\n entrypoint: new URL(\"../src/Gone.astro\", import.meta.url),\n prerender: true,\n });\n }\n // Each permalink's help, at `<name>.json` beside its address, and each older line's\n // under the line's base path. One route for each folder: the current line's, then each\n // older line's.\n for (const pattern of helpPatterns(\n entries.map((entry) => entry.siteRoot),\n astro.base,\n )) {\n injectRoute({\n pattern,\n entrypoint: new URL(\"./help-route.js\", import.meta.url),\n prerender: true,\n });\n }\n }\n\n const projectHome = (entry: Entry) =>\n homeRoute(entry.project.consumer.basePath, astro.trailingSlash);\n // Dev serving of `_ascribe/files/`, and of the plain output for agents; the build copies them in `astro:build:done`.\n updateConfig({\n ...supplied,\n ...(format.supplied === undefined ? {} : { build: { format: format.supplied } }),\n ...(options.codeTitles === false\n ? {}\n : { markdown: { shikiConfig: { transformers: [codeTitles] } } }),\n vite: {\n // `Elements.astro` is Astro source, so Vite must compile it, not load it as a Node module.\n ssr: { noExternal: [\"@ascribed/astro\"] },\n plugins: [\n {\n name: \"@ascribed/astro:site\",\n resolveId: (id) => (id === SITE_MODULE ? `\\0${SITE_MODULE}` : undefined),\n load: (id) =>\n id === `\\0${SITE_MODULE}`\n ? `export const siteRoot = ${JSON.stringify(entries[0]?.siteRoot ?? \"\")};\\n` +\n `export const astroRoot = ${JSON.stringify(root)};\\n` +\n `export const home = ${JSON.stringify(homeRoute(astro.base, astro.trailingSlash))};\\n` +\n `export const base = ${JSON.stringify(astro.base)};\\n` +\n `export const collections = ${JSON.stringify(\n named ? Object.fromEntries(entries.map((e) => [e.name, e.siteRoot])) : {},\n )};\\n` +\n `export const permalinks = ${JSON.stringify(\n entries.map((e) => ({ siteRoot: e.siteRoot, home: projectHome(e) })),\n )};\\n` +\n `export const routes = ${JSON.stringify(\n entries.map((e) => ({\n siteRoot: e.siteRoot,\n basePath: e.project.consumer.basePath,\n trailingSlash: e.project.consumer.trailingSlash,\n })),\n )};`\n : undefined,\n },\n {\n name: \"@ascribed/astro:files\",\n configureServer(server) {\n for (const entry of entries) {\n const base = entry.project.consumer.basePath;\n server.middlewares.use(filesMiddleware(entry.siteRoot, base));\n // Each older line's, at its own base path; the lines are read as each request comes.\n server.middlewares.use((req, res, next) =>\n inOrder(\n olderLines(entry.siteRoot).map((line) =>\n filesMiddleware(line.siteRoot, line.base),\n ),\n req,\n res,\n next,\n ),\n );\n if (entry.agentsRoot !== undefined) {\n const siteRoot = entry.siteRoot;\n server.middlewares.use(\n agentsMiddleware(entry.agentsRoot, base, (pathname) =>\n headersFor(siteRoot, pathname),\n ),\n );\n // Each older line's outputs for agents, at its own base path.\n server.middlewares.use((req, res, next) =>\n inOrder(\n olderLines(siteRoot).map((line) =>\n agentsMiddleware(line.plainRoot, line.base, (pathname) =>\n headersFor(line.siteRoot, pathname),\n ),\n ),\n req,\n res,\n next,\n ),\n );\n }\n }\n // The sitemap and robots.txt, as the build would write them now.\n server.middlewares.use(\n rootFilesMiddleware(astro.base, () =>\n rootFiles(\n rootSources(\n devSitePages(astroRoutes, astro),\n devRouted(astroRoutes, astro),\n ),\n ),\n ),\n );\n },\n },\n ],\n },\n });\n },\n \"astro:routes:resolved\": ({ routes }) => {\n astroRoutes = routes;\n },\n \"astro:server:setup\": ({ server, refreshContent, logger, toolbar }) => {\n if (!devCommand || !routing) return;\n if (!refreshContent)\n throw new Error(\"@ascribed/astro: this Astro version does not support refreshContent.\");\n devHttps = Boolean(server.config.server.https);\n for (const group of groups) {\n group.builds = watchDev({\n server,\n seen,\n refreshContent: () => refreshContent({}),\n logger,\n project: group.project,\n astro: routing,\n builds: group.entries.map((entry) => entry.build),\n rebuild: group.rebuild,\n ...(binary === undefined ? {} : { binary: { binary, root: group.project.dir } }),\n onBuilt: () => {\n if (reviewed !== undefined && group.entries.includes(reviewed)) review?.rebuilt();\n },\n });\n }\n const group = groups.find((g) => reviewed !== undefined && g.entries.includes(reviewed));\n if (reviewed !== undefined && group?.builds !== undefined && binary !== undefined) {\n const entry = reviewed;\n const builds = group.builds;\n const run = { binary, configPath: entry.project.configPath, cwd: entry.project.dir };\n // A versioned build is reviewed in every line, the current one first, each line's\n // routes from its own JSON output.\n const lines = () =>\n linesOf(entry.siteRoot)?.map((line) => ({\n build: line.build,\n siteRoot: line.siteRoot,\n })) ?? [{ build: entry.build, siteRoot: entry.siteRoot }];\n const lineBuilds = () => lines().map((line) => line.build);\n review = new ReviewServer({\n channel: toolbar as ToolbarChannel,\n logger,\n build: entry.build,\n contentRoot: entry.project.contentRoot,\n channelProblem: channelProblem(server.config),\n diff: (base) => runDiff({ ...run, build: lineBuilds(), base }),\n prompt: (page, base) => runPrompt({ ...run, build: lineBuilds(), base, path: page }),\n projectDir: entry.project.dir,\n connect: async () => (await import(\"./review/github.js\")).connect(entry.project.dir),\n writeRoutes: () =>\n builds.inTurn(async () => {\n await runBuild({ ...run, build: lineBuilds(), outputs: [\"json\"] });\n }),\n readRoutes: async () => {\n const all = new Map<string, RoutedPage>();\n const versioned = lines().length > 1;\n for (const line of lines()) {\n const jsonRoot = path.join(path.dirname(line.siteRoot), \"json\");\n for (const [route, page] of await readRoutes(jsonRoot)) {\n if (!all.has(route))\n all.set(route, versioned ? { ...page, build: line.build } : page);\n }\n }\n return all;\n },\n });\n }\n },\n \"astro:server:start\": ({ address, logger }) => {\n review?.listening(address.address);\n if (!devCommand) return;\n // Each project's folder gets its own file, with its own address: its base path.\n for (const group of groups) {\n const entry = group.entries[0] as Entry;\n const dir = group.project.dir;\n try {\n writeDevFile(dir, {\n url: siteUrl(address, { https: devHttps, base: entry.project.consumer.basePath }),\n build: entry.build,\n pid: process.pid,\n });\n // Vite exits on a signal before `astro:server:done` runs.\n process.once(\"exit\", () => removeDevFile(dir, process.pid));\n } catch (error) {\n logger.warn(\n `couldn't write ${groups.length > 1 ? path.join(dir, \".ascribe\", \"dev.json\") : \".ascribe/dev.json\"}, which the editor's Open Site Preview reads: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n },\n \"astro:server:done\": () => {\n review?.dispose();\n review = undefined;\n if (devCommand) for (const group of groups) removeDevFile(group.project.dir, process.pid);\n },\n \"astro:build:done\": async ({ dir, pages, logger }) => {\n const base = routing?.base ?? \"/\";\n const trailingSlash = routing?.trailingSlash ?? \"ignore\";\n // Every page Astro built, by its route from the host's root. Astro lists\n // pages only, not endpoints such as a feed, so a dot in a page's last\n // segment, as in `/v1.2`, is still a page.\n const built = pages.map((page) =>\n routeAs(`${normalizeBase(base)}${page.pathname.replace(/^\\//, \"\")}`, trailingSlash),\n );\n const builtKeys = new Set(built.map(routeKey));\n const files = rootFiles(rootSources(built, (route) => builtKeys.has(routeKey(route))));\n fail(files.problems, \"the site's robots.txt\");\n for (const note of files.notes) logger[note.level](note.text);\n const out = fileURLToPath(dir);\n if (files.sitemap !== undefined) {\n await writeFile(path.join(out, \"sitemap.xml\"), files.sitemap);\n logger.info(`wrote ${sitemapRoute(base)}`);\n }\n if (files.robots !== undefined) {\n await writeFile(path.join(out, \"robots.txt\"), files.robots);\n logger.info(\"wrote /robots.txt\");\n }\n // The host's files, from the projects that name it: each build's,\n // and each of its older lines'.\n const hostRoots = entries\n .filter((entry) => entry.project.consumer.host === \"netlify\")\n .flatMap((entry) => [\n entry.siteRoot,\n ...olderLines(entry.siteRoot).map((line) => line.siteRoot),\n ]);\n const ownHeaders = path.join(publicDir, \"_headers\");\n const headers = headersOutcome({\n rules: mergeRules(hostRoots.map((siteRoot) => readHostHeaders(siteRoot) ?? [])),\n base,\n own: existsSync(ownHeaders) ? readFileSync(ownHeaders, \"utf8\") : undefined,\n });\n if (headers.kind === \"fail\") throw new Error(`@ascribed/astro: ${headers.text}`);\n if (headers.kind === \"note\") logger.info(headers.text);\n if (headers.kind === \"write\") {\n await writeFile(path.join(out, \"_headers\"), headers.text);\n logger.info(\"wrote /_headers\");\n }\n const ownRedirects = path.join(publicDir, \"_redirects\");\n const redirects = redirectsOutcome({\n redirects: mergeRedirects(hostRoots.map(readHostRedirects)),\n base,\n published: (route) => builtKeys.has(routeKey(route)),\n own: existsSync(ownRedirects) ? readFileSync(ownRedirects, \"utf8\") : undefined,\n });\n if (redirects.kind === \"fail\") throw new Error(`@ascribed/astro: ${redirects.text}`);\n if (redirects.kind === \"note\") logger.info(redirects.text);\n if (redirects.kind === \"write\") {\n await writeFile(path.join(out, \"_redirects\"), redirects.text);\n logger.info(\"wrote /_redirects\");\n }\n // Entries of one project publish at one base path: the first entry's copies go last, so they win.\n for (const entry of [...entries].reverse()) {\n const folder = folderUnder(entry.project.consumer.basePath, base);\n const where = folder === \"\" ? \"\" : ` to ${folder}`;\n if (await copyPublishedFiles(entry.siteRoot, dir, folder))\n logger.info(`copied _ascribe/files/${where}`);\n for (const line of olderLines(entry.siteRoot)) {\n const lineFolder = folderUnder(line.base, base);\n if (await copyPublishedFiles(line.siteRoot, dir, lineFolder))\n logger.info(`copied ${line.build}'s _ascribe/files/ to ${lineFolder}`);\n }\n if (\n entry.agentsRoot !== undefined &&\n (await copyAgentFiles(entry.agentsRoot, dir, folder))\n )\n logger.info(`copied llms.txt and each page's Markdown${where}`);\n if (entry.agentsRoot !== undefined) {\n for (const line of olderLines(entry.siteRoot)) {\n const lineFolder = folderUnder(line.base, base);\n if (await copyAgentFiles(line.plainRoot, dir, lineFolder))\n logger.info(\n `copied ${line.build}'s llms.txt and each page's Markdown to ${lineFolder}`,\n );\n }\n }\n }\n },\n },\n };\n}\n"]}
package/dist/names.d.ts CHANGED
@@ -24,6 +24,16 @@ export declare const CLASS_AVAILABILITY_LEAD_IN = "ascribe-availability-lead-in"
24
24
  * (packages/elements/CONTRACT.md §9).
25
25
  */
26
26
  export declare const CLASS_CODE_TESTED = "ascribe-code-tested";
27
+ /**
28
+ * `<ascribe-operation>`: an operation of a declared API, from `@operation`
29
+ * (packages/elements/CONTRACT.md §11).
30
+ */
31
+ export declare const ELEMENT_OPERATION = "ascribe-operation";
32
+ /**
33
+ * On the `(deprecated)` after a deprecated operation's link in
34
+ * `<ascribe-operation>` (packages/elements/CONTRACT.md §11).
35
+ */
36
+ export declare const CLASS_OPERATION_DEPRECATED = "ascribe-operation-deprecated";
27
37
  /** `<ascribe-group>`: a project widget's group (packages/elements/CONTRACT.md §6). */
28
38
  export declare const ELEMENT_GROUP = "ascribe-group";
29
39
  /**
package/dist/names.js CHANGED
@@ -27,6 +27,16 @@ export const CLASS_AVAILABILITY_LEAD_IN = "ascribe-availability-lead-in";
27
27
  * (packages/elements/CONTRACT.md §9).
28
28
  */
29
29
  export const CLASS_CODE_TESTED = "ascribe-code-tested";
30
+ /**
31
+ * `<ascribe-operation>`: an operation of a declared API, from `@operation`
32
+ * (packages/elements/CONTRACT.md §11).
33
+ */
34
+ export const ELEMENT_OPERATION = "ascribe-operation";
35
+ /**
36
+ * On the `(deprecated)` after a deprecated operation's link in
37
+ * `<ascribe-operation>` (packages/elements/CONTRACT.md §11).
38
+ */
39
+ export const CLASS_OPERATION_DEPRECATED = "ascribe-operation-deprecated";
30
40
  /** `<ascribe-group>`: a project widget's group (packages/elements/CONTRACT.md §6). */
31
41
  export const ELEMENT_GROUP = "ascribe-group";
32
42
  /**
package/dist/names.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"names.js","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"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","sourcesContent":["// The names Ascribe puts on a page: elements, attributes, classes, and ids.\n// Generated from crates/ascribe-core/src/names.rs by\n// `ASCRIBE_BLESS=1 cargo test -p ascribe-core --test names`. Don't edit it.\n\n/** `<ascribe-note>`: a note (packages/elements/CONTRACT.md §1). */\nexport const ELEMENT_NOTE = \"ascribe-note\";\n\n/** `<ascribe-steps>`: a procedure (packages/elements/CONTRACT.md §2). */\nexport const ELEMENT_STEPS = \"ascribe-steps\";\n\n/** `<ascribe-tabs>`: a variant group of several arms (packages/elements/CONTRACT.md §3). */\nexport const ELEMENT_TABS = \"ascribe-tabs\";\n\n/** `<ascribe-tab>`: one arm of `<ascribe-tabs>` (packages/elements/CONTRACT.md §3). */\nexport const ELEMENT_TAB = \"ascribe-tab\";\n\n/** `<ascribe-availability>`: a badge or filter (packages/elements/CONTRACT.md §4). */\nexport const ELEMENT_AVAILABILITY = \"ascribe-availability\";\n\n/**\n * `<ascribe-availability-target>`: one target of `<ascribe-availability>`\n * (packages/elements/CONTRACT.md §4).\n */\nexport const ELEMENT_AVAILABILITY_TARGET = \"ascribe-availability-target\";\n\n/**\n * On the visually hidden `(Available: ` and `)` around a table row's\n * `<ascribe-availability>` targets (packages/elements/CONTRACT.md §4).\n */\nexport const CLASS_AVAILABILITY_LEAD_IN = \"ascribe-availability-lead-in\";\n\n/**\n * On the line of a code block's caption that says what a tested example\n * was tested with; its lead-in, \"Tested with\", is the stylesheet's\n * (packages/elements/CONTRACT.md §9).\n */\nexport const CLASS_CODE_TESTED = \"ascribe-code-tested\";\n\n/** `<ascribe-group>`: a project widget's group (packages/elements/CONTRACT.md §6). */\nexport const ELEMENT_GROUP = \"ascribe-group\";\n\n/**\n * `<ascribe-for-agents>`: the visually hidden pointer to `llms.txt` at the top\n * of a page, for agents (packages/elements/CONTRACT.md §8).\n */\nexport const ELEMENT_FOR_AGENTS = \"ascribe-for-agents\";\n\n/** `<ascribe-attributes>`: the attribute marker (site-render contract §1). */\nexport const ELEMENT_ATTRIBUTES = \"ascribe-attributes\";\n\n/** The name an anchor comment starts with, `<!--ascribe-anchor` (site-render contract §7.3). */\nexport const COMMENT_ANCHOR = \"ascribe-anchor\";\n\n/** A block's source anchor (site-render contract §7.1). */\nexport const DATA_SOURCE = \"data-ascribe-source\";\n\n/** The includes a block came through (site-render contract §7.1). */\nexport const DATA_VIA = \"data-ascribe-via\";\n\n/** A glossary term's id, on its link (packages/elements/CONTRACT.md §7). */\nexport const DATA_TERM = \"data-ascribe-term\";\n\n/** The line a link out of a line leads to, on its link (packages/elements/CONTRACT.md §10). */\nexport const DATA_RELEASE_LINE = \"data-ascribe-release-line\";\n\n/** The element the report draws into. */\nexport const ID_REPORT = \"ascribe-review\";\n\n/** The `<script type=\"application/json\">` holding the report's data. */\nexport const ID_REPORT_DATA = \"ascribe-review-data\";\n\n/**\n * The page's light or dark scheme, `light` or `dark`, on the root\n * element (docs/content/astro/reference.md).\n */\nexport const DATA_SCHEME = \"data-ascribe-scheme\";\n\n/** On a marked page's root: what it shows, `changes`, `will`, or `was`. */\nexport const DATA_SHOW = \"data-ascribe-show\";\n\n/**\n * On a marked block: its change, `added`, `changed`, `removed`, `moved`,\n * or `moved-from`.\n */\nexport const DATA_CHANGE = \"data-ascribe-change\";\n\n/** On every element review adds to the page, so it can be taken away again. */\nexport const DATA_UI = \"data-ascribe-ui\";\n\n/** On a change's label: the change. */\nexport const DATA_LABEL = \"data-ascribe-label\";\n\n/** On a tab's hint: `added` or `changed`. */\nexport const DATA_HINT = \"data-ascribe-hint\";\n\n/** On a moved block: the id its \"moved from\" link points at. */\nexport const DATA_MOVE = \"data-ascribe-move\";\n\n/** On a changed block whose copy as it was follows it. */\nexport const DATA_HAS_WAS = \"data-ascribe-has-was\";\n\n/** On a copy of a block as it was, shown only with the page as it was. */\nexport const DATA_WAS_ONLY = \"data-ascribe-was-only\";\n\n/** On a changed, removed, or moved block: its source anchor as it was. */\nexport const DATA_WAS_SOURCE = \"data-ascribe-was-source\";\n\n/** On the elements that hold the threads' overlay. */\nexport const DATA_OVERLAY = \"data-ascribe-overlay\";\n\n/** On the style element the Astro dev toolbar adds for the marks. */\nexport const DATA_REVIEW = \"data-ascribe-review\";\n\n/** On the root of a page whose changes are marked. */\nexport const CLASS_MARKS = \"ascribe-marks\";\n\n/** A change's label. */\nexport const CLASS_LABEL = \"ascribe-label\";\n\n/** A label placed before its block rather than over it. */\nexport const CLASS_LABEL_BEFORE = \"ascribe-label-before\";\n\n/** Inserted text. */\nexport const CLASS_INS = \"ascribe-ins\";\n\n/** Deleted text. */\nexport const CLASS_DEL = \"ascribe-del\";\n\n/** A removed block. */\nexport const CLASS_REMOVED = \"ascribe-removed\";\n\n/** A removed block's content. */\nexport const CLASS_REMOVED_BODY = \"ascribe-removed-body\";\n\n/** The button that shows or collapses a removed block. */\nexport const CLASS_TOGGLE = \"ascribe-toggle\";\n\n/** On a removed block that's shown. */\nexport const CLASS_OPEN = \"ascribe-open\";\n\n/** What stands where a moved block was. */\nexport const CLASS_MOVED_FROM = \"ascribe-moved-from\";\n\n/** The note with a moved block's link. */\nexport const CLASS_MOVE_NOTE = \"ascribe-move-note\";\n\n/** A move note after its block, not inside it. */\nexport const CLASS_MOVE_AFTER = \"ascribe-move-after\";\n\n/** The link between a moved block and where it was. */\nexport const CLASS_MOVE_LINK = \"ascribe-move-link\";\n\n/** A moved block's copy as it was, where it was. */\nexport const CLASS_WAS_COPY = \"ascribe-was-copy\";\n\n/** A tab's hint that it holds changes. */\nexport const CLASS_HINT = \"ascribe-hint\";\n\n/** A block flashed after going to it. */\nexport const CLASS_FLASH = \"ascribe-flash\";\n\n/** The tip that shows a block's source on hover and focus. */\nexport const CLASS_WHERE = \"ascribe-where\";\n"]}
1
+ {"version":3,"file":"names.js","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"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","sourcesContent":["// The names Ascribe puts on a page: elements, attributes, classes, and ids.\n// Generated from crates/ascribe-core/src/names.rs by\n// `ASCRIBE_BLESS=1 cargo test -p ascribe-core --test names`. Don't edit it.\n\n/** `<ascribe-note>`: a note (packages/elements/CONTRACT.md §1). */\nexport const ELEMENT_NOTE = \"ascribe-note\";\n\n/** `<ascribe-steps>`: a procedure (packages/elements/CONTRACT.md §2). */\nexport const ELEMENT_STEPS = \"ascribe-steps\";\n\n/** `<ascribe-tabs>`: a variant group of several arms (packages/elements/CONTRACT.md §3). */\nexport const ELEMENT_TABS = \"ascribe-tabs\";\n\n/** `<ascribe-tab>`: one arm of `<ascribe-tabs>` (packages/elements/CONTRACT.md §3). */\nexport const ELEMENT_TAB = \"ascribe-tab\";\n\n/** `<ascribe-availability>`: a badge or filter (packages/elements/CONTRACT.md §4). */\nexport const ELEMENT_AVAILABILITY = \"ascribe-availability\";\n\n/**\n * `<ascribe-availability-target>`: one target of `<ascribe-availability>`\n * (packages/elements/CONTRACT.md §4).\n */\nexport const ELEMENT_AVAILABILITY_TARGET = \"ascribe-availability-target\";\n\n/**\n * On the visually hidden `(Available: ` and `)` around a table row's\n * `<ascribe-availability>` targets (packages/elements/CONTRACT.md §4).\n */\nexport const CLASS_AVAILABILITY_LEAD_IN = \"ascribe-availability-lead-in\";\n\n/**\n * On the line of a code block's caption that says what a tested example\n * was tested with; its lead-in, \"Tested with\", is the stylesheet's\n * (packages/elements/CONTRACT.md §9).\n */\nexport const CLASS_CODE_TESTED = \"ascribe-code-tested\";\n\n/**\n * `<ascribe-operation>`: an operation of a declared API, from `@operation`\n * (packages/elements/CONTRACT.md §11).\n */\nexport const ELEMENT_OPERATION = \"ascribe-operation\";\n\n/**\n * On the `(deprecated)` after a deprecated operation's link in\n * `<ascribe-operation>` (packages/elements/CONTRACT.md §11).\n */\nexport const CLASS_OPERATION_DEPRECATED = \"ascribe-operation-deprecated\";\n\n/** `<ascribe-group>`: a project widget's group (packages/elements/CONTRACT.md §6). */\nexport const ELEMENT_GROUP = \"ascribe-group\";\n\n/**\n * `<ascribe-for-agents>`: the visually hidden pointer to `llms.txt` at the top\n * of a page, for agents (packages/elements/CONTRACT.md §8).\n */\nexport const ELEMENT_FOR_AGENTS = \"ascribe-for-agents\";\n\n/** `<ascribe-attributes>`: the attribute marker (site-render contract §1). */\nexport const ELEMENT_ATTRIBUTES = \"ascribe-attributes\";\n\n/** The name an anchor comment starts with, `<!--ascribe-anchor` (site-render contract §7.3). */\nexport const COMMENT_ANCHOR = \"ascribe-anchor\";\n\n/** A block's source anchor (site-render contract §7.1). */\nexport const DATA_SOURCE = \"data-ascribe-source\";\n\n/** The includes a block came through (site-render contract §7.1). */\nexport const DATA_VIA = \"data-ascribe-via\";\n\n/** A glossary term's id, on its link (packages/elements/CONTRACT.md §7). */\nexport const DATA_TERM = \"data-ascribe-term\";\n\n/** The line a link out of a line leads to, on its link (packages/elements/CONTRACT.md §10). */\nexport const DATA_RELEASE_LINE = \"data-ascribe-release-line\";\n\n/** The element the report draws into. */\nexport const ID_REPORT = \"ascribe-review\";\n\n/** The `<script type=\"application/json\">` holding the report's data. */\nexport const ID_REPORT_DATA = \"ascribe-review-data\";\n\n/**\n * The page's light or dark scheme, `light` or `dark`, on the root\n * element (docs/content/astro/reference.md).\n */\nexport const DATA_SCHEME = \"data-ascribe-scheme\";\n\n/** On a marked page's root: what it shows, `changes`, `will`, or `was`. */\nexport const DATA_SHOW = \"data-ascribe-show\";\n\n/**\n * On a marked block: its change, `added`, `changed`, `removed`, `moved`,\n * or `moved-from`.\n */\nexport const DATA_CHANGE = \"data-ascribe-change\";\n\n/** On every element review adds to the page, so it can be taken away again. */\nexport const DATA_UI = \"data-ascribe-ui\";\n\n/** On a change's label: the change. */\nexport const DATA_LABEL = \"data-ascribe-label\";\n\n/** On a tab's hint: `added` or `changed`. */\nexport const DATA_HINT = \"data-ascribe-hint\";\n\n/** On a moved block: the id its \"moved from\" link points at. */\nexport const DATA_MOVE = \"data-ascribe-move\";\n\n/** On a changed block whose copy as it was follows it. */\nexport const DATA_HAS_WAS = \"data-ascribe-has-was\";\n\n/** On a copy of a block as it was, shown only with the page as it was. */\nexport const DATA_WAS_ONLY = \"data-ascribe-was-only\";\n\n/** On a changed, removed, or moved block: its source anchor as it was. */\nexport const DATA_WAS_SOURCE = \"data-ascribe-was-source\";\n\n/** On the elements that hold the threads' overlay. */\nexport const DATA_OVERLAY = \"data-ascribe-overlay\";\n\n/** On the style element the Astro dev toolbar adds for the marks. */\nexport const DATA_REVIEW = \"data-ascribe-review\";\n\n/** On the root of a page whose changes are marked. */\nexport const CLASS_MARKS = \"ascribe-marks\";\n\n/** A change's label. */\nexport const CLASS_LABEL = \"ascribe-label\";\n\n/** A label placed before its block rather than over it. */\nexport const CLASS_LABEL_BEFORE = \"ascribe-label-before\";\n\n/** Inserted text. */\nexport const CLASS_INS = \"ascribe-ins\";\n\n/** Deleted text. */\nexport const CLASS_DEL = \"ascribe-del\";\n\n/** A removed block. */\nexport const CLASS_REMOVED = \"ascribe-removed\";\n\n/** A removed block's content. */\nexport const CLASS_REMOVED_BODY = \"ascribe-removed-body\";\n\n/** The button that shows or collapses a removed block. */\nexport const CLASS_TOGGLE = \"ascribe-toggle\";\n\n/** On a removed block that's shown. */\nexport const CLASS_OPEN = \"ascribe-open\";\n\n/** What stands where a moved block was. */\nexport const CLASS_MOVED_FROM = \"ascribe-moved-from\";\n\n/** The note with a moved block's link. */\nexport const CLASS_MOVE_NOTE = \"ascribe-move-note\";\n\n/** A move note after its block, not inside it. */\nexport const CLASS_MOVE_AFTER = \"ascribe-move-after\";\n\n/** The link between a moved block and where it was. */\nexport const CLASS_MOVE_LINK = \"ascribe-move-link\";\n\n/** A moved block's copy as it was, where it was. */\nexport const CLASS_WAS_COPY = \"ascribe-was-copy\";\n\n/** A tab's hint that it holds changes. */\nexport const CLASS_HINT = \"ascribe-hint\";\n\n/** A block flashed after going to it. */\nexport const CLASS_FLASH = \"ascribe-flash\";\n\n/** The tip that shows a block's source on hover and focus. */\nexport const CLASS_WHERE = \"ascribe-where\";\n"]}
package/dist/project.d.ts CHANGED
@@ -15,8 +15,8 @@ export interface ProjectInfo {
15
15
  trailingSlash: "always" | "never";
16
16
  /** Whether builds publish what agents read: `llms.txt` and each page's Markdown, in the plain output. */
17
17
  agents: boolean;
18
- /** The host whose format the site's response headers are written in: `"netlify"`, or none. */
19
- headers: "netlify" | undefined;
18
+ /** The host the site is deployed to, whose files the compiler writes: `"netlify"`, or none. */
19
+ host: "netlify" | undefined;
20
20
  };
21
21
  /** The names of `[builds]`'s tables, in order: empty when there are none, and the one build is `site`. */
22
22
  builds: string[];
@@ -69,6 +69,21 @@ export declare function supplyRouting(projects: readonly ProjectInfo[], astro: A
69
69
  routing: AstroRouting;
70
70
  supplied: Partial<AstroRouting>;
71
71
  };
72
+ /** Astro's `build.format`: how a page's route becomes the file a static host serves it from. */
73
+ type BuildFormat = "file" | "directory" | "preserve";
74
+ /**
75
+ * Astro's `build.format` for routes that end as `trailingSlash` says, and
76
+ * the format to supply for `updateConfig`. A static host serves `x/index.html`
77
+ * at `x/` and `x.html` at `x`, so `"never"` needs `"file"` and `"always"`
78
+ * needs `"directory"`; any other format serves each page behind a redirect.
79
+ * Astro can't tell an integration a format left out from its default,
80
+ * `"directory"`, so that one is replaced for `"never"`; any other that
81
+ * disagrees is a problem, as a sentence.
82
+ */
83
+ export declare function supplyBuildFormat(trailingSlash: "always" | "never" | "ignore", format: BuildFormat): {
84
+ supplied: BuildFormat | undefined;
85
+ problems: string[];
86
+ };
72
87
  /**
73
88
  * The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,
74
89
  * as sentences. Ascribe writes every link with its own settings, so a site
@@ -79,3 +94,4 @@ export declare function consumerMismatches(project: ProjectInfo, astro: {
79
94
  trailingSlash: string;
80
95
  site: string | undefined;
81
96
  }): string[];
97
+ export {};
package/dist/project.js CHANGED
@@ -48,7 +48,7 @@ export function readProject(dir, binary) {
48
48
  trailingSlash,
49
49
  // `true` with a site unless it says otherwise, as the binary reads it.
50
50
  agents: typeof consumer["agents"] === "boolean" ? consumer["agents"] : site !== undefined,
51
- headers: consumer["headers"] === "netlify" ? "netlify" : undefined,
51
+ host: consumer["host"] === "netlify" ? "netlify" : undefined,
52
52
  },
53
53
  builds,
54
54
  siteRoot: (build) => path.resolve(dir, outputDir, build, "site"),
@@ -142,6 +142,30 @@ export function supplyRouting(projects, astro, supplyBase) {
142
142
  supplied.base = base;
143
143
  return { routing: { ...astro, ...supplied }, supplied };
144
144
  }
145
+ /**
146
+ * Astro's `build.format` for routes that end as `trailingSlash` says, and
147
+ * the format to supply for `updateConfig`. A static host serves `x/index.html`
148
+ * at `x/` and `x.html` at `x`, so `"never"` needs `"file"` and `"always"`
149
+ * needs `"directory"`; any other format serves each page behind a redirect.
150
+ * Astro can't tell an integration a format left out from its default,
151
+ * `"directory"`, so that one is replaced for `"never"`; any other that
152
+ * disagrees is a problem, as a sentence.
153
+ */
154
+ export function supplyBuildFormat(trailingSlash, format) {
155
+ if (trailingSlash === "ignore")
156
+ return { supplied: undefined, problems: [] };
157
+ const wanted = trailingSlash === "never" ? "file" : "directory";
158
+ if (format === wanted)
159
+ return { supplied: undefined, problems: [] };
160
+ if (format === "directory")
161
+ return { supplied: wanted, problems: [] };
162
+ return {
163
+ supplied: undefined,
164
+ problems: [
165
+ `[consumer] trailing-slash is "${trailingSlash}", but Astro's \`build.format\` is "${format}", which a static host serves behind a redirect. Use "${wanted}", or leave it out.`,
166
+ ],
167
+ };
168
+ }
145
169
  /**
146
170
  * The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,
147
171
  * as sentences. Ascribe writes every link with its own settings, so a site
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["// What the integration needs from `ascribe.toml`: where a build's site output\n// goes, and the consumer settings that must agree with `astro.config`.\n// The compiler validates the whole file; this reads\n// only what it compiled. A file with `[from]` takes keys from another\n// project's, which only the binary follows, so then the binary is asked.\nimport { execFileSync } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { parse } from \"smol-toml\";\nimport { findBinary } from \"./binary.js\";\n\n/** The parts of an Ascribe project the integration reads. */\nexport interface ProjectInfo {\n /** The directory holding `ascribe.toml`. */\n dir: string;\n /** The path of `ascribe.toml`. */\n configPath: string;\n /** Source files and assets watched during development. */\n contentRoot: string;\n /** Generated output, never treated as a source change. */\n outputRoot: string;\n /** `[consumer]`, with the profile's defaults. */\n consumer: {\n site: string | undefined;\n basePath: string;\n trailingSlash: \"always\" | \"never\";\n /** Whether builds publish what agents read: `llms.txt` and each page's Markdown, in the plain output. */\n agents: boolean;\n /** The host whose format the site's response headers are written in: `\"netlify\"`, or none. */\n headers: \"netlify\" | undefined;\n };\n /** The names of `[builds]`'s tables, in order: empty when there are none, and the one build is `site`. */\n builds: string[];\n /** The site output's root for a build: `<output-dir>/<build>/site`. */\n siteRoot(build: string): string;\n /** The plain output's root for a build: `<output-dir>/<build>/plain`. */\n plainRoot(build: string): string;\n}\n\n/** Where to find the binary, as the integration's `binary` option and Astro's root say. */\nexport interface BinaryOption {\n binary?: string | undefined;\n root: string;\n}\n\n/**\n * Reads `ascribe.toml` in `dir`. Throws a readable error if it can't be read.\n * When it has `[from]`, what it takes is the other file's, which the binary\n * reads: `[consumer]` and `[builds]` are then the binary's answer, from\n * `ascribe model --format json`, run with the binary `binary` finds\n * (`findBinary`).\n */\nexport function readProject(dir: string, binary?: BinaryOption): ProjectInfo {\n const configPath = path.join(dir, \"ascribe.toml\");\n let table: Record<string, unknown>;\n try {\n table = parse(readFileSync(configPath, \"utf8\"));\n } catch (error) {\n throw new Error(\n `@ascribed/astro can't read ${configPath}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n const project = section(table, \"project\");\n let consumer = section(table, \"consumer\");\n let builds = Object.keys(section(table, \"builds\"));\n if (Object.keys(section(table, \"from\")).length > 0) {\n const model = askModel(configPath, findBinary(binary ?? { root: dir }));\n consumer = Object.fromEntries(\n model.consumer.filter((key) => key.set).map((key) => [key.key, key.value]),\n );\n if (takes(table, \"builds\")) builds = model.builds.map((build) => build.name);\n }\n const outputDir =\n typeof project[\"output-dir\"] === \"string\" ? project[\"output-dir\"] : \".ascribe/build\";\n const contentDir = typeof project[\"content-root\"] === \"string\" ? project[\"content-root\"] : \"docs\";\n const trailingSlash = consumer[\"trailing-slash\"] === \"never\" ? \"never\" : \"always\";\n const site = typeof consumer[\"site\"] === \"string\" ? consumer[\"site\"] : undefined;\n return {\n dir,\n configPath,\n contentRoot: path.resolve(dir, contentDir),\n outputRoot: path.resolve(dir, outputDir),\n consumer: {\n site,\n basePath: normalizeBase(\n typeof consumer[\"base-path\"] === \"string\" ? consumer[\"base-path\"] : \"/\",\n ),\n trailingSlash,\n // `true` with a site unless it says otherwise, as the binary reads it.\n agents: typeof consumer[\"agents\"] === \"boolean\" ? consumer[\"agents\"] : site !== undefined,\n headers: consumer[\"headers\"] === \"netlify\" ? \"netlify\" : undefined,\n },\n builds,\n siteRoot: (build) => path.resolve(dir, outputDir, build, \"site\"),\n plainRoot: (build) => path.resolve(dir, outputDir, build, \"plain\"),\n };\n}\n\n/** What the integration reads of `ascribe model --format json`. */\ninterface ModelReport {\n consumer: { key: string; value?: unknown; set: boolean }[];\n builds: { name: string; line?: string }[];\n}\n\n/** The content model as the binary reads it, `[from]` followed. Throws a readable error when it can't. */\nfunction askModel(configPath: string, binary: string): ModelReport {\n try {\n const out = execFileSync(binary, [\"model\", \"--format\", \"json\", \"--config\", configPath], {\n encoding: \"utf8\",\n maxBuffer: 64 * 1024 * 1024,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n const report = JSON.parse(out) as Partial<ModelReport>;\n return {\n consumer: report.consumer ?? [],\n builds: (report.builds ?? []).filter((build) => build.line === undefined),\n };\n } catch (error) {\n const stderr = (error as { stderr?: unknown }).stderr;\n const detail =\n typeof stderr === \"string\" && stderr.trim() !== \"\"\n ? stderr.trim()\n : error instanceof Error\n ? error.message\n : String(error);\n throw new Error(\n `@ascribed/astro can't read ${configPath}, whose [from] takes keys from another file, with \\`ascribe model\\`: ${detail}`,\n { cause: error },\n );\n }\n}\n\n/** Whether a `take` list of `[from]` names `name`, or a key in it. */\nfunction takes(table: Record<string, unknown>, name: string): boolean {\n return Object.values(section(table, \"from\")).some((from) => {\n const take = typeof from === \"object\" && from !== null ? (from as { take?: unknown }).take : [];\n return (\n Array.isArray(take) &&\n take.some((key) => typeof key === \"string\" && (key === name || key.startsWith(`${name}.`)))\n );\n });\n}\n\nfunction section(table: Record<string, unknown>, name: string): Record<string, unknown> {\n const value = table[name];\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/** A base path with a leading and a trailing `/`, as Ascribe's router has it. */\nexport function normalizeBase(base: string): string {\n let result = base.trim();\n if (!result.startsWith(\"/\")) result = `/${result}`;\n if (!result.endsWith(\"/\")) result += \"/\";\n return result;\n}\n\n/**\n * The route of a base path, ending the way `trailingSlash` says the site's\n * routes do: the site's root for Astro's `base`, a project's root for its\n * `[consumer] base-path`. (`import.meta.env.BASE_URL` isn't replaced in a\n * package's components.)\n */\nexport function homeRoute(base: string, trailingSlash: \"always\" | \"never\" | \"ignore\"): string {\n const root = normalizeBase(base);\n return trailingSlash === \"never\" && root !== \"/\" ? root.slice(0, -1) : root;\n}\n\n/** Astro's routing settings, as the integration reads and supplies them. */\nexport interface AstroRouting {\n base: string;\n trailingSlash: \"always\" | \"never\" | \"ignore\";\n site: string | undefined;\n}\n\n/**\n * Astro's routing with what astro.config leaves unset taken from `[consumer]`,\n * and the settings taken, for `updateConfig`. Astro gives an integration its\n * configuration with its defaults filled in, so unset is the default: no\n * `site`, `trailingSlash: \"ignore\"`, and `base: \"/\"`. `site` and\n * `trailingSlash` come from the first project that has them, since every\n * project must agree with them anyway. `base` comes from the first project's\n * `base-path` only with `supplyBase`, as with one project; with several, it\n * stays the site's, the root every base path is under. A base Astro has is\n * rewritten to end the way a supplied `trailingSlash` says, as Astro writes\n * it when the setting is its own.\n */\nexport function supplyRouting(\n projects: readonly ProjectInfo[],\n astro: AstroRouting,\n supplyBase: boolean,\n): { routing: AstroRouting; supplied: Partial<AstroRouting> } {\n const supplied: Partial<AstroRouting> = {};\n if (astro.site === undefined) {\n const site = projects.find((project) => project.consumer.site !== undefined)?.consumer.site;\n if (site !== undefined) supplied.site = site;\n }\n const first = projects[0];\n if (first === undefined) return { routing: astro, supplied };\n if (astro.trailingSlash === \"ignore\") supplied.trailingSlash = first.consumer.trailingSlash;\n const trailingSlash = supplied.trailingSlash ?? astro.trailingSlash;\n const base = homeRoute(\n supplyBase && normalizeBase(astro.base) === \"/\" ? first.consumer.basePath : astro.base,\n trailingSlash,\n );\n if (base !== astro.base) supplied.base = base;\n return { routing: { ...astro, ...supplied }, supplied };\n}\n\n/**\n * The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,\n * as sentences. Ascribe writes every link with its own settings, so a site\n * whose routes differ would have broken links.\n */\nexport function consumerMismatches(\n project: ProjectInfo,\n astro: { base: string; trailingSlash: string; site: string | undefined },\n): string[] {\n const problems: string[] = [];\n const { basePath, trailingSlash, site } = project.consumer;\n // A base path under Astro's `base` leaves the rest of the site to other pages, such as a home page.\n if (!basePath.startsWith(normalizeBase(astro.base))) {\n problems.push(\n `[consumer] base-path is \"${basePath}\", but Astro's \\`base\\` is \"${astro.base}\". Set base-path to that path or a path under it.`,\n );\n }\n // Astro's \"ignore\" serves both forms, so Ascribe's links work either way.\n if (astro.trailingSlash !== \"ignore\" && astro.trailingSlash !== trailingSlash) {\n problems.push(\n `[consumer] trailing-slash is \"${trailingSlash}\", but Astro's \\`trailingSlash\\` is \"${astro.trailingSlash}\".`,\n );\n }\n if (site !== undefined && astro.site !== undefined && origin(site) !== origin(astro.site)) {\n problems.push(`[consumer] site is \"${site}\", but Astro's \\`site\\` is \"${astro.site}\".`);\n }\n return problems;\n}\n\nfunction origin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return url;\n }\n}\n"]}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["// What the integration needs from `ascribe.toml`: where a build's site output\n// goes, and the consumer settings that must agree with `astro.config`.\n// The compiler validates the whole file; this reads\n// only what it compiled. A file with `[from]` takes keys from another\n// project's, which only the binary follows, so then the binary is asked.\nimport { execFileSync } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { parse } from \"smol-toml\";\nimport { findBinary } from \"./binary.js\";\n\n/** The parts of an Ascribe project the integration reads. */\nexport interface ProjectInfo {\n /** The directory holding `ascribe.toml`. */\n dir: string;\n /** The path of `ascribe.toml`. */\n configPath: string;\n /** Source files and assets watched during development. */\n contentRoot: string;\n /** Generated output, never treated as a source change. */\n outputRoot: string;\n /** `[consumer]`, with the profile's defaults. */\n consumer: {\n site: string | undefined;\n basePath: string;\n trailingSlash: \"always\" | \"never\";\n /** Whether builds publish what agents read: `llms.txt` and each page's Markdown, in the plain output. */\n agents: boolean;\n /** The host the site is deployed to, whose files the compiler writes: `\"netlify\"`, or none. */\n host: \"netlify\" | undefined;\n };\n /** The names of `[builds]`'s tables, in order: empty when there are none, and the one build is `site`. */\n builds: string[];\n /** The site output's root for a build: `<output-dir>/<build>/site`. */\n siteRoot(build: string): string;\n /** The plain output's root for a build: `<output-dir>/<build>/plain`. */\n plainRoot(build: string): string;\n}\n\n/** Where to find the binary, as the integration's `binary` option and Astro's root say. */\nexport interface BinaryOption {\n binary?: string | undefined;\n root: string;\n}\n\n/**\n * Reads `ascribe.toml` in `dir`. Throws a readable error if it can't be read.\n * When it has `[from]`, what it takes is the other file's, which the binary\n * reads: `[consumer]` and `[builds]` are then the binary's answer, from\n * `ascribe model --format json`, run with the binary `binary` finds\n * (`findBinary`).\n */\nexport function readProject(dir: string, binary?: BinaryOption): ProjectInfo {\n const configPath = path.join(dir, \"ascribe.toml\");\n let table: Record<string, unknown>;\n try {\n table = parse(readFileSync(configPath, \"utf8\"));\n } catch (error) {\n throw new Error(\n `@ascribed/astro can't read ${configPath}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n const project = section(table, \"project\");\n let consumer = section(table, \"consumer\");\n let builds = Object.keys(section(table, \"builds\"));\n if (Object.keys(section(table, \"from\")).length > 0) {\n const model = askModel(configPath, findBinary(binary ?? { root: dir }));\n consumer = Object.fromEntries(\n model.consumer.filter((key) => key.set).map((key) => [key.key, key.value]),\n );\n if (takes(table, \"builds\")) builds = model.builds.map((build) => build.name);\n }\n const outputDir =\n typeof project[\"output-dir\"] === \"string\" ? project[\"output-dir\"] : \".ascribe/build\";\n const contentDir = typeof project[\"content-root\"] === \"string\" ? project[\"content-root\"] : \"docs\";\n const trailingSlash = consumer[\"trailing-slash\"] === \"never\" ? \"never\" : \"always\";\n const site = typeof consumer[\"site\"] === \"string\" ? consumer[\"site\"] : undefined;\n return {\n dir,\n configPath,\n contentRoot: path.resolve(dir, contentDir),\n outputRoot: path.resolve(dir, outputDir),\n consumer: {\n site,\n basePath: normalizeBase(\n typeof consumer[\"base-path\"] === \"string\" ? consumer[\"base-path\"] : \"/\",\n ),\n trailingSlash,\n // `true` with a site unless it says otherwise, as the binary reads it.\n agents: typeof consumer[\"agents\"] === \"boolean\" ? consumer[\"agents\"] : site !== undefined,\n host: consumer[\"host\"] === \"netlify\" ? \"netlify\" : undefined,\n },\n builds,\n siteRoot: (build) => path.resolve(dir, outputDir, build, \"site\"),\n plainRoot: (build) => path.resolve(dir, outputDir, build, \"plain\"),\n };\n}\n\n/** What the integration reads of `ascribe model --format json`. */\ninterface ModelReport {\n consumer: { key: string; value?: unknown; set: boolean }[];\n builds: { name: string; line?: string }[];\n}\n\n/** The content model as the binary reads it, `[from]` followed. Throws a readable error when it can't. */\nfunction askModel(configPath: string, binary: string): ModelReport {\n try {\n const out = execFileSync(binary, [\"model\", \"--format\", \"json\", \"--config\", configPath], {\n encoding: \"utf8\",\n maxBuffer: 64 * 1024 * 1024,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n const report = JSON.parse(out) as Partial<ModelReport>;\n return {\n consumer: report.consumer ?? [],\n builds: (report.builds ?? []).filter((build) => build.line === undefined),\n };\n } catch (error) {\n const stderr = (error as { stderr?: unknown }).stderr;\n const detail =\n typeof stderr === \"string\" && stderr.trim() !== \"\"\n ? stderr.trim()\n : error instanceof Error\n ? error.message\n : String(error);\n throw new Error(\n `@ascribed/astro can't read ${configPath}, whose [from] takes keys from another file, with \\`ascribe model\\`: ${detail}`,\n { cause: error },\n );\n }\n}\n\n/** Whether a `take` list of `[from]` names `name`, or a key in it. */\nfunction takes(table: Record<string, unknown>, name: string): boolean {\n return Object.values(section(table, \"from\")).some((from) => {\n const take = typeof from === \"object\" && from !== null ? (from as { take?: unknown }).take : [];\n return (\n Array.isArray(take) &&\n take.some((key) => typeof key === \"string\" && (key === name || key.startsWith(`${name}.`)))\n );\n });\n}\n\nfunction section(table: Record<string, unknown>, name: string): Record<string, unknown> {\n const value = table[name];\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/** A base path with a leading and a trailing `/`, as Ascribe's router has it. */\nexport function normalizeBase(base: string): string {\n let result = base.trim();\n if (!result.startsWith(\"/\")) result = `/${result}`;\n if (!result.endsWith(\"/\")) result += \"/\";\n return result;\n}\n\n/**\n * The route of a base path, ending the way `trailingSlash` says the site's\n * routes do: the site's root for Astro's `base`, a project's root for its\n * `[consumer] base-path`. (`import.meta.env.BASE_URL` isn't replaced in a\n * package's components.)\n */\nexport function homeRoute(base: string, trailingSlash: \"always\" | \"never\" | \"ignore\"): string {\n const root = normalizeBase(base);\n return trailingSlash === \"never\" && root !== \"/\" ? root.slice(0, -1) : root;\n}\n\n/** Astro's routing settings, as the integration reads and supplies them. */\nexport interface AstroRouting {\n base: string;\n trailingSlash: \"always\" | \"never\" | \"ignore\";\n site: string | undefined;\n}\n\n/**\n * Astro's routing with what astro.config leaves unset taken from `[consumer]`,\n * and the settings taken, for `updateConfig`. Astro gives an integration its\n * configuration with its defaults filled in, so unset is the default: no\n * `site`, `trailingSlash: \"ignore\"`, and `base: \"/\"`. `site` and\n * `trailingSlash` come from the first project that has them, since every\n * project must agree with them anyway. `base` comes from the first project's\n * `base-path` only with `supplyBase`, as with one project; with several, it\n * stays the site's, the root every base path is under. A base Astro has is\n * rewritten to end the way a supplied `trailingSlash` says, as Astro writes\n * it when the setting is its own.\n */\nexport function supplyRouting(\n projects: readonly ProjectInfo[],\n astro: AstroRouting,\n supplyBase: boolean,\n): { routing: AstroRouting; supplied: Partial<AstroRouting> } {\n const supplied: Partial<AstroRouting> = {};\n if (astro.site === undefined) {\n const site = projects.find((project) => project.consumer.site !== undefined)?.consumer.site;\n if (site !== undefined) supplied.site = site;\n }\n const first = projects[0];\n if (first === undefined) return { routing: astro, supplied };\n if (astro.trailingSlash === \"ignore\") supplied.trailingSlash = first.consumer.trailingSlash;\n const trailingSlash = supplied.trailingSlash ?? astro.trailingSlash;\n const base = homeRoute(\n supplyBase && normalizeBase(astro.base) === \"/\" ? first.consumer.basePath : astro.base,\n trailingSlash,\n );\n if (base !== astro.base) supplied.base = base;\n return { routing: { ...astro, ...supplied }, supplied };\n}\n\n/** Astro's `build.format`: how a page's route becomes the file a static host serves it from. */\ntype BuildFormat = \"file\" | \"directory\" | \"preserve\";\n\n/**\n * Astro's `build.format` for routes that end as `trailingSlash` says, and\n * the format to supply for `updateConfig`. A static host serves `x/index.html`\n * at `x/` and `x.html` at `x`, so `\"never\"` needs `\"file\"` and `\"always\"`\n * needs `\"directory\"`; any other format serves each page behind a redirect.\n * Astro can't tell an integration a format left out from its default,\n * `\"directory\"`, so that one is replaced for `\"never\"`; any other that\n * disagrees is a problem, as a sentence.\n */\nexport function supplyBuildFormat(\n trailingSlash: \"always\" | \"never\" | \"ignore\",\n format: BuildFormat,\n): { supplied: BuildFormat | undefined; problems: string[] } {\n if (trailingSlash === \"ignore\") return { supplied: undefined, problems: [] };\n const wanted = trailingSlash === \"never\" ? \"file\" : \"directory\";\n if (format === wanted) return { supplied: undefined, problems: [] };\n if (format === \"directory\") return { supplied: wanted, problems: [] };\n return {\n supplied: undefined,\n problems: [\n `[consumer] trailing-slash is \"${trailingSlash}\", but Astro's \\`build.format\\` is \"${format}\", which a static host serves behind a redirect. Use \"${wanted}\", or leave it out.`,\n ],\n };\n}\n\n/**\n * The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,\n * as sentences. Ascribe writes every link with its own settings, so a site\n * whose routes differ would have broken links.\n */\nexport function consumerMismatches(\n project: ProjectInfo,\n astro: { base: string; trailingSlash: string; site: string | undefined },\n): string[] {\n const problems: string[] = [];\n const { basePath, trailingSlash, site } = project.consumer;\n // A base path under Astro's `base` leaves the rest of the site to other pages, such as a home page.\n if (!basePath.startsWith(normalizeBase(astro.base))) {\n problems.push(\n `[consumer] base-path is \"${basePath}\", but Astro's \\`base\\` is \"${astro.base}\". Set base-path to that path or a path under it.`,\n );\n }\n // Astro's \"ignore\" serves both forms, so Ascribe's links work either way.\n if (astro.trailingSlash !== \"ignore\" && astro.trailingSlash !== trailingSlash) {\n problems.push(\n `[consumer] trailing-slash is \"${trailingSlash}\", but Astro's \\`trailingSlash\\` is \"${astro.trailingSlash}\".`,\n );\n }\n if (site !== undefined && astro.site !== undefined && origin(site) !== origin(astro.site)) {\n problems.push(`[consumer] site is \"${site}\", but Astro's \\`site\\` is \"${astro.site}\".`);\n }\n return problems;\n}\n\nfunction origin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return url;\n }\n}\n"]}
@@ -0,0 +1,69 @@
1
+ /** One redirect: an old address, from the host's root, to where it leads now. */
2
+ export interface Redirect {
3
+ from: string;
4
+ to: string;
5
+ status: number;
6
+ }
7
+ /**
8
+ * The redirects in the host's `_redirects` the compiler wrote in a build's
9
+ * site output, in its order: none when it has none, as without `[consumer]
10
+ * host`.
11
+ */
12
+ export declare function readHostRedirects(siteRoot: string): Redirect[];
13
+ /**
14
+ * Every project's redirects as one list, in the order given: an address two
15
+ * projects both redirect keeps the first's, as the first entry's files win.
16
+ */
17
+ export declare function mergeRedirects(projects: readonly (readonly Redirect[])[]): Redirect[];
18
+ /** The redirects in Netlify's `_redirects` format: each on a line, `from`, `to`, and the status. */
19
+ export declare function netlifyRedirects(redirects: readonly Redirect[]): string;
20
+ /** What the build does about the host's redirects. */
21
+ export type RedirectsOutcome = {
22
+ kind: "none";
23
+ } | {
24
+ kind: "write";
25
+ text: string;
26
+ } | {
27
+ kind: "note";
28
+ text: string;
29
+ } | {
30
+ kind: "fail";
31
+ text: string;
32
+ };
33
+ /** What the build does about `_redirects`, given every project's redirects and the site. */
34
+ export declare function redirectsOutcome(sources: {
35
+ /** The redirects of every project that sets `[consumer] host = "netlify"`, merged. */
36
+ redirects: readonly Redirect[];
37
+ /** Astro's `base`. */
38
+ base: string;
39
+ /** Whether a route is a page the site publishes, which a redirect mustn't hide. */
40
+ published: (route: string) => boolean;
41
+ /** The site's own `public/_redirects`, when it has one. */
42
+ own: string | undefined;
43
+ }): RedirectsOutcome;
44
+ /** What a retired address's page says: why, and where the docs' home is. */
45
+ export interface GonePage {
46
+ reason: string;
47
+ home: string;
48
+ }
49
+ /**
50
+ * The route patterns the retired addresses' pages need, one for each
51
+ * folder that has any, under `base`.
52
+ */
53
+ export declare function gonePatterns(siteRoots: readonly string[], base: string): string[];
54
+ /**
55
+ * `getStaticPaths` for the route at `routePattern`, one of `gonePatterns`:
56
+ * one page for each retired address's page in its folder, with the reason
57
+ * and the route of its project's root.
58
+ */
59
+ export declare function gonePaths(sources: readonly {
60
+ siteRoot: string;
61
+ home: string;
62
+ }[], base: string, routePattern: string): {
63
+ params: {
64
+ address: string;
65
+ };
66
+ props: GonePage;
67
+ }[];
68
+ /** The routes of the retired addresses' pages the site outputs at `siteRoots` list, from the host's root. */
69
+ export declare function goneRoutes(siteRoots: readonly string[]): string[];
@@ -0,0 +1,165 @@
1
+ // The redirects a build writes for the host: with `[consumer] host =
2
+ // "netlify"`, the compiler writes Netlify's `_redirects` in each build's site
3
+ // output, at `_ascribe/host/_redirects` (output-layout contract §1.9), from
4
+ // the moves `ascribe.addresses` records. `astro build` merges every
5
+ // project's into the site's `_redirects`, at the root of what's deployed.
6
+ //
7
+ // A site's own `public/_redirects` is kept, after Ascribe's rules: Netlify
8
+ // uses the first rule that matches, so a page's recorded move wins over a
9
+ // pattern of the site's.
10
+ import { readFileSync } from "node:fs";
11
+ import path from "node:path";
12
+ import { normalizeBase } from "./project.js";
13
+ import { routeKey } from "./records.js";
14
+ /**
15
+ * The redirects in the host's `_redirects` the compiler wrote in a build's
16
+ * site output, in its order: none when it has none, as without `[consumer]
17
+ * host`.
18
+ */
19
+ export function readHostRedirects(siteRoot) {
20
+ let text;
21
+ try {
22
+ text = readFileSync(path.join(siteRoot, "_ascribe", "host", "_redirects"), "utf8");
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ const out = [];
28
+ for (const line of text.split(/\r?\n/)) {
29
+ const trimmed = line.trim();
30
+ if (trimmed === "" || trimmed.startsWith("#"))
31
+ continue;
32
+ const [from, to, status] = trimmed.split(/\s+/);
33
+ if (from === undefined || to === undefined)
34
+ continue;
35
+ out.push({ from, to, status: status === undefined ? 301 : Number.parseInt(status, 10) });
36
+ }
37
+ return out;
38
+ }
39
+ /**
40
+ * Every project's redirects as one list, in the order given: an address two
41
+ * projects both redirect keeps the first's, as the first entry's files win.
42
+ */
43
+ export function mergeRedirects(projects) {
44
+ const seen = new Set();
45
+ const out = [];
46
+ for (const list of projects) {
47
+ for (const redirect of list) {
48
+ if (seen.has(redirect.from))
49
+ continue;
50
+ seen.add(redirect.from);
51
+ out.push(redirect);
52
+ }
53
+ }
54
+ return out;
55
+ }
56
+ /** The redirects in Netlify's `_redirects` format: each on a line, `from`, `to`, and the status. */
57
+ export function netlifyRedirects(redirects) {
58
+ return redirects.map((r) => `${r.from} ${r.to} ${String(r.status)}\n`).join("");
59
+ }
60
+ /** What the build does about `_redirects`, given every project's redirects and the site. */
61
+ export function redirectsOutcome(sources) {
62
+ if (sources.redirects.length === 0)
63
+ return { kind: "none" };
64
+ const text = netlifyRedirects(sources.redirects);
65
+ const base = normalizeBase(sources.base);
66
+ if (base !== "/") {
67
+ return {
68
+ kind: "note",
69
+ text: `no _redirects: Astro's \`base\` is "${base}", and Netlify reads _redirects only at the root of what's deployed. Put these lines in the host's own:\n${text}`,
70
+ };
71
+ }
72
+ const taken = sources.redirects.filter((r) => sources.published(routeKey(r.from)));
73
+ if (taken.length > 0) {
74
+ return {
75
+ kind: "fail",
76
+ text: `a page the site publishes is at an address ascribe.addresses forwards somewhere else, so its redirect would hide the page or never be used. Remove the entry, or move the page:\n${netlifyRedirects(taken)}`,
77
+ };
78
+ }
79
+ if (sources.own === undefined)
80
+ return { kind: "write", text };
81
+ const own = sources.own.endsWith("\n") || sources.own === "" ? sources.own : `${sources.own}\n`;
82
+ return {
83
+ kind: "write",
84
+ text: `${text}\n# The site's own, from public/_redirects: after Ascribe's, since the first rule that matches is used.\n${own}`,
85
+ };
86
+ }
87
+ /** The folder a retired address's page is in, under its build's base path. */
88
+ const GONE_FOLDER = "/_ascribe/gone/";
89
+ /** The format version of `_ascribe/redirects.json` this reads. */
90
+ const REDIRECTS_VERSION = 1;
91
+ /** The retired addresses a build's site output lists: none when it has no `_ascribe/redirects.json`. */
92
+ function readGone(siteRoot) {
93
+ const file = path.join(siteRoot, "_ascribe", "redirects.json");
94
+ let text;
95
+ try {
96
+ text = readFileSync(file, "utf8");
97
+ }
98
+ catch {
99
+ return [];
100
+ }
101
+ const list = JSON.parse(text);
102
+ if (list.version !== REDIRECTS_VERSION) {
103
+ throw new Error(`@ascribed/astro: ${file} is version ${String(list.version)} of the redirects list, and this version of the integration reads version ${String(REDIRECTS_VERSION)}. Update @ascribed/astro and @ascribed/cli together.`);
104
+ }
105
+ return list.gone ?? [];
106
+ }
107
+ /**
108
+ * Each retired address's page the site outputs list, by its route from the
109
+ * host's root, with its folder (its build's base path and `_ascribe/gone/`),
110
+ * its address from there, and what it says. A page two projects both list
111
+ * keeps the first's.
112
+ */
113
+ function gonePages(sources) {
114
+ const pages = new Map();
115
+ for (const { siteRoot, home } of sources) {
116
+ for (const gone of readGone(siteRoot)) {
117
+ const at = gone.page.indexOf(GONE_FOLDER);
118
+ if (at < 0)
119
+ continue;
120
+ const folder = gone.page.slice(0, at + GONE_FOLDER.length);
121
+ const address = gone.page.slice(folder.length).replace(/\/+$/, "");
122
+ if (address === "")
123
+ continue;
124
+ const route = `${folder}${address}`;
125
+ if (!pages.has(route))
126
+ pages.set(route, { folder, address, props: { reason: gone.reason, home } });
127
+ }
128
+ }
129
+ return pages;
130
+ }
131
+ /**
132
+ * The route pattern for the pages in `folder`, as Astro takes it: under
133
+ * `base`, which Astro adds. Throws when the folder isn't under `base`, since
134
+ * an Astro site serves nothing outside it.
135
+ */
136
+ function gonePattern(folder, base) {
137
+ const root = normalizeBase(base);
138
+ if (!folder.startsWith(root)) {
139
+ throw new Error(`@ascribed/astro: retired addresses' pages are published at "${folder}", which isn't under Astro's \`base\`, "${base}", and an Astro site serves nothing outside it. Set [consumer] base-path in ascribe.toml to a path under "${root}".`);
140
+ }
141
+ return `/${folder.slice(root.length)}[...address]`;
142
+ }
143
+ /**
144
+ * The route patterns the retired addresses' pages need, one for each
145
+ * folder that has any, under `base`.
146
+ */
147
+ export function gonePatterns(siteRoots, base) {
148
+ const pages = gonePages(siteRoots.map((siteRoot) => ({ siteRoot, home: "/" })));
149
+ return [...new Set([...pages.values()].map(({ folder }) => gonePattern(folder, base)))];
150
+ }
151
+ /**
152
+ * `getStaticPaths` for the route at `routePattern`, one of `gonePatterns`:
153
+ * one page for each retired address's page in its folder, with the reason
154
+ * and the route of its project's root.
155
+ */
156
+ export function gonePaths(sources, base, routePattern) {
157
+ return [...gonePages(sources).values()]
158
+ .filter(({ folder }) => gonePattern(folder, base) === routePattern)
159
+ .map(({ address, props }) => ({ params: { address }, props }));
160
+ }
161
+ /** The routes of the retired addresses' pages the site outputs at `siteRoots` list, from the host's root. */
162
+ export function goneRoutes(siteRoots) {
163
+ return [...gonePages(siteRoots.map((siteRoot) => ({ siteRoot, home: "/" }))).keys()];
164
+ }
165
+ //# sourceMappingURL=redirects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirects.js","sourceRoot":"","sources":["../src/redirects.ts"],"names":[],"mappings":"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","sourcesContent":["// The redirects a build writes for the host: with `[consumer] host =\n// \"netlify\"`, the compiler writes Netlify's `_redirects` in each build's site\n// output, at `_ascribe/host/_redirects` (output-layout contract §1.9), from\n// the moves `ascribe.addresses` records. `astro build` merges every\n// project's into the site's `_redirects`, at the root of what's deployed.\n//\n// A site's own `public/_redirects` is kept, after Ascribe's rules: Netlify\n// uses the first rule that matches, so a page's recorded move wins over a\n// pattern of the site's.\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { normalizeBase } from \"./project.js\";\nimport { routeKey } from \"./records.js\";\n\n/** One redirect: an old address, from the host's root, to where it leads now. */\nexport interface Redirect {\n from: string;\n to: string;\n status: number;\n}\n\n/**\n * The redirects in the host's `_redirects` the compiler wrote in a build's\n * site output, in its order: none when it has none, as without `[consumer]\n * host`.\n */\nexport function readHostRedirects(siteRoot: string): Redirect[] {\n let text: string;\n try {\n text = readFileSync(path.join(siteRoot, \"_ascribe\", \"host\", \"_redirects\"), \"utf8\");\n } catch {\n return [];\n }\n const out: Redirect[] = [];\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (trimmed === \"\" || trimmed.startsWith(\"#\")) continue;\n const [from, to, status] = trimmed.split(/\\s+/);\n if (from === undefined || to === undefined) continue;\n out.push({ from, to, status: status === undefined ? 301 : Number.parseInt(status, 10) });\n }\n return out;\n}\n\n/**\n * Every project's redirects as one list, in the order given: an address two\n * projects both redirect keeps the first's, as the first entry's files win.\n */\nexport function mergeRedirects(projects: readonly (readonly Redirect[])[]): Redirect[] {\n const seen = new Set<string>();\n const out: Redirect[] = [];\n for (const list of projects) {\n for (const redirect of list) {\n if (seen.has(redirect.from)) continue;\n seen.add(redirect.from);\n out.push(redirect);\n }\n }\n return out;\n}\n\n/** The redirects in Netlify's `_redirects` format: each on a line, `from`, `to`, and the status. */\nexport function netlifyRedirects(redirects: readonly Redirect[]): string {\n return redirects.map((r) => `${r.from} ${r.to} ${String(r.status)}\\n`).join(\"\");\n}\n\n/** What the build does about the host's redirects. */\nexport type RedirectsOutcome =\n | { kind: \"none\" }\n | { kind: \"write\"; text: string }\n | { kind: \"note\"; text: string }\n | { kind: \"fail\"; text: string };\n\n/** What the build does about `_redirects`, given every project's redirects and the site. */\nexport function redirectsOutcome(sources: {\n /** The redirects of every project that sets `[consumer] host = \"netlify\"`, merged. */\n redirects: readonly Redirect[];\n /** Astro's `base`. */\n base: string;\n /** Whether a route is a page the site publishes, which a redirect mustn't hide. */\n published: (route: string) => boolean;\n /** The site's own `public/_redirects`, when it has one. */\n own: string | undefined;\n}): RedirectsOutcome {\n if (sources.redirects.length === 0) return { kind: \"none\" };\n const text = netlifyRedirects(sources.redirects);\n const base = normalizeBase(sources.base);\n if (base !== \"/\") {\n return {\n kind: \"note\",\n text: `no _redirects: Astro's \\`base\\` is \"${base}\", and Netlify reads _redirects only at the root of what's deployed. Put these lines in the host's own:\\n${text}`,\n };\n }\n const taken = sources.redirects.filter((r) => sources.published(routeKey(r.from)));\n if (taken.length > 0) {\n return {\n kind: \"fail\",\n text: `a page the site publishes is at an address ascribe.addresses forwards somewhere else, so its redirect would hide the page or never be used. Remove the entry, or move the page:\\n${netlifyRedirects(taken)}`,\n };\n }\n if (sources.own === undefined) return { kind: \"write\", text };\n const own = sources.own.endsWith(\"\\n\") || sources.own === \"\" ? sources.own : `${sources.own}\\n`;\n return {\n kind: \"write\",\n text: `${text}\\n# The site's own, from public/_redirects: after Ascribe's, since the first rule that matches is used.\\n${own}`,\n };\n}\n\n/** The folder a retired address's page is in, under its build's base path. */\nconst GONE_FOLDER = \"/_ascribe/gone/\";\n\n/** The format version of `_ascribe/redirects.json` this reads. */\nconst REDIRECTS_VERSION = 1;\n\n/** A retired address, as a build's `_ascribe/redirects.json` lists it (output-layout contract §1.8). */\ninterface Gone {\n /** The address, from the host's root. */\n path: string;\n /** Why it was retired, as its authors wrote it. */\n reason: string;\n /** The `ascribe.addresses` key it comes from. */\n entry: string;\n /** Where its page is, from the host's root: the base path, `_ascribe/gone/`, and the address from there. */\n page: string;\n}\n\n/** What a retired address's page says: why, and where the docs' home is. */\nexport interface GonePage {\n reason: string;\n home: string;\n}\n\n/** The retired addresses a build's site output lists: none when it has no `_ascribe/redirects.json`. */\nfunction readGone(siteRoot: string): Gone[] {\n const file = path.join(siteRoot, \"_ascribe\", \"redirects.json\");\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return [];\n }\n const list = JSON.parse(text) as { version: number; gone?: Gone[] };\n if (list.version !== REDIRECTS_VERSION) {\n throw new Error(\n `@ascribed/astro: ${file} is version ${String(list.version)} of the redirects list, and this version of the integration reads version ${String(REDIRECTS_VERSION)}. Update @ascribed/astro and @ascribed/cli together.`,\n );\n }\n return list.gone ?? [];\n}\n\n/**\n * Each retired address's page the site outputs list, by its route from the\n * host's root, with its folder (its build's base path and `_ascribe/gone/`),\n * its address from there, and what it says. A page two projects both list\n * keeps the first's.\n */\nfunction gonePages(\n sources: readonly { siteRoot: string; home: string }[],\n): Map<string, { folder: string; address: string; props: GonePage }> {\n const pages = new Map<string, { folder: string; address: string; props: GonePage }>();\n for (const { siteRoot, home } of sources) {\n for (const gone of readGone(siteRoot)) {\n const at = gone.page.indexOf(GONE_FOLDER);\n if (at < 0) continue;\n const folder = gone.page.slice(0, at + GONE_FOLDER.length);\n const address = gone.page.slice(folder.length).replace(/\\/+$/, \"\");\n if (address === \"\") continue;\n const route = `${folder}${address}`;\n if (!pages.has(route))\n pages.set(route, { folder, address, props: { reason: gone.reason, home } });\n }\n }\n return pages;\n}\n\n/**\n * The route pattern for the pages in `folder`, as Astro takes it: under\n * `base`, which Astro adds. Throws when the folder isn't under `base`, since\n * an Astro site serves nothing outside it.\n */\nfunction gonePattern(folder: string, base: string): string {\n const root = normalizeBase(base);\n if (!folder.startsWith(root)) {\n throw new Error(\n `@ascribed/astro: retired addresses' pages are published at \"${folder}\", which isn't under Astro's \\`base\\`, \"${base}\", and an Astro site serves nothing outside it. Set [consumer] base-path in ascribe.toml to a path under \"${root}\".`,\n );\n }\n return `/${folder.slice(root.length)}[...address]`;\n}\n\n/**\n * The route patterns the retired addresses' pages need, one for each\n * folder that has any, under `base`.\n */\nexport function gonePatterns(siteRoots: readonly string[], base: string): string[] {\n const pages = gonePages(siteRoots.map((siteRoot) => ({ siteRoot, home: \"/\" })));\n return [...new Set([...pages.values()].map(({ folder }) => gonePattern(folder, base)))];\n}\n\n/**\n * `getStaticPaths` for the route at `routePattern`, one of `gonePatterns`:\n * one page for each retired address's page in its folder, with the reason\n * and the route of its project's root.\n */\nexport function gonePaths(\n sources: readonly { siteRoot: string; home: string }[],\n base: string,\n routePattern: string,\n): { params: { address: string }; props: GonePage }[] {\n return [...gonePages(sources).values()]\n .filter(({ folder }) => gonePattern(folder, base) === routePattern)\n .map(({ address, props }) => ({ params: { address }, props }));\n}\n\n/** The routes of the retired addresses' pages the site outputs at `siteRoots` list, from the host's root. */\nexport function goneRoutes(siteRoots: readonly string[]): string[] {\n return [...gonePages(siteRoots.map((siteRoot) => ({ siteRoot, home: \"/\" }))).keys()];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ascribed/astro",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "A toolchain for documentation written as code. This package is its Astro integration, which publishes a project's pages as a site.",
5
5
  "type": "module",
6
6
  "license": "MPL-2.0",
@@ -30,6 +30,7 @@
30
30
  "src/Elements.astro",
31
31
  "src/Availability.astro",
32
32
  "src/Permalink.astro",
33
+ "src/Gone.astro",
33
34
  "src/MissingPermalink.astro",
34
35
  "src/Head.astro",
35
36
  "src/Versions.astro",
@@ -61,15 +62,19 @@
61
62
  "./permalinks": {
62
63
  "types": "./dist/permalinks.d.ts",
63
64
  "default": "./dist/permalinks.js"
65
+ },
66
+ "./redirects": {
67
+ "types": "./dist/redirects.d.ts",
68
+ "default": "./dist/redirects.js"
64
69
  }
65
70
  },
66
71
  "engines": {
67
72
  "node": ">=24"
68
73
  },
69
74
  "dependencies": {
70
- "@ascribed/cli": "0.4.3",
71
- "@ascribed/elements": "0.4.3",
72
- "@ascribed/review": "0.4.3",
75
+ "@ascribed/cli": "0.4.4",
76
+ "@ascribed/elements": "0.4.4",
77
+ "@ascribed/review": "0.4.4",
73
78
  "github-slugger": "^2.0.0",
74
79
  "property-information": "^7.2.0",
75
80
  "smol-toml": "^1.9.1"
package/src/Gone.astro ADDED
@@ -0,0 +1,37 @@
1
+ ---
2
+ // A retired address's page: it says what was here was removed, gives the
3
+ // reason as its authors wrote it, as text, and links to the docs' home: the
4
+ // root of the project whose `_ascribe/redirects.json` lists it. The
5
+ // integration adds it as a route at `<base path>_ascribe/gone/[...address]`
6
+ // for each build's base path, one page for each retired address there, and
7
+ // the host's `_redirects` serves it at the address with a 404. Which folder
8
+ // this is comes from the route's pattern. It isn't indexed.
9
+ import type { GetStaticPaths } from "astro";
10
+ import { base, permalinks } from "virtual:ascribe/site";
11
+ import { gonePaths, type GonePage } from "@ascribed/astro/redirects";
12
+
13
+ export const getStaticPaths = (({ routePattern }) =>
14
+ gonePaths(permalinks, base, routePattern)) satisfies GetStaticPaths;
15
+
16
+ const { reason, home } = Astro.props as GonePage;
17
+ ---
18
+
19
+ <!doctype html>
20
+ <html lang="en">
21
+ <head>
22
+ <meta charset="utf-8" />
23
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
24
+ <title>Removed from these docs</title>
25
+ <meta name="robots" content="noindex" />
26
+ </head>
27
+ <body>
28
+ <main>
29
+ <h1>Removed from these docs</h1>
30
+ <p>The page that was here was removed from these docs.</p>
31
+ <p data-reason>{reason}</p>
32
+ <p>
33
+ <a href={home}>Go to the docs' home page</a>
34
+ </p>
35
+ </main>
36
+ </body>
37
+ </html>