@heroiclands/package-build 25.6.3 → 25.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +19 -1
  3. package/bin/content-commands.mjs +14 -70
  4. package/bin/package-build.mjs +25 -10
  5. package/ci/wip-workflows.test.mjs +252 -0
  6. package/content-config.mjs +40 -12
  7. package/docs/api.md +237 -48
  8. package/docs/commands.md +73 -36
  9. package/docs/configuration.md +92 -48
  10. package/docs/guides/release-and-deploy.md +26 -14
  11. package/docs/guides/site.md +29 -6
  12. package/engine/cdn-publish.mjs +164 -6
  13. package/engine/content-index.mjs +71 -19
  14. package/engine/content-spans.mjs +18 -1
  15. package/engine/content-styles.mjs +85 -0
  16. package/engine/derived-sections.mjs +4 -3
  17. package/engine/event-views.mjs +6 -4
  18. package/engine/helpers.mjs +33 -9
  19. package/engine/index.mjs +1 -0
  20. package/engine/note-cache.mjs +1107 -0
  21. package/engine/note-page.mjs +424 -0
  22. package/engine/pack-config.mjs +47 -4
  23. package/engine/pathnames.mjs +22 -7
  24. package/engine/prepared-only.mjs +32 -0
  25. package/engine/preview-index.mjs +55 -13
  26. package/engine/site-build.mjs +34 -448
  27. package/engine/site-config.mjs +33 -4
  28. package/engine/site-maps.mjs +16 -24
  29. package/engine/site-origins.mjs +23 -0
  30. package/engine/site-page.mjs +648 -0
  31. package/engine/site-render.mjs +142 -10
  32. package/engine/site-serve.mjs +1 -1
  33. package/engine/sql-tables.mjs +12 -3
  34. package/package.json +6 -1
  35. package/site-theme/static/css/style.css +14 -0
  36. package/types/content-config.d.mts +24 -4
  37. package/types/engine/cdn-publish.d.mts +22 -0
  38. package/types/engine/content-index.d.mts +36 -2
  39. package/types/engine/content-styles.d.mts +20 -0
  40. package/types/engine/derived-sections.d.mts +3 -3
  41. package/types/engine/event-views.d.mts +4 -3
  42. package/types/engine/helpers.d.mts +18 -1
  43. package/types/engine/index.d.mts +1 -0
  44. package/types/engine/note-cache.d.mts +149 -0
  45. package/types/engine/note-page.d.mts +92 -0
  46. package/types/engine/pack-config.d.mts +30 -0
  47. package/types/engine/prepared-only.d.mts +14 -0
  48. package/types/engine/preview-index.d.mts +35 -0
  49. package/types/engine/site-build.d.mts +5 -100
  50. package/types/engine/site-config.d.mts +3 -2
  51. package/types/engine/site-maps.d.mts +7 -11
  52. package/types/engine/site-origins.d.mts +9 -0
  53. package/types/engine/site-page.d.mts +261 -0
  54. package/types/engine/site-render.d.mts +20 -1
  55. package/types/engine/sql-tables.d.mts +8 -4
@@ -60,6 +60,7 @@ export * as assetReferences from "./asset-references.mjs";
60
60
  export * as packages from "./packages.mjs";
61
61
  export * as packagebuildIndex from "./packagebuild-index.mjs";
62
62
  export * as siteBuild from "./site-build.mjs";
63
+ export * as sitePage from "./site-page.mjs";
63
64
  export * as sitePreview from "./site-preview.mjs";
64
65
  export * as previewIndex from "./preview-index.mjs";
65
66
  export * as contentRender from "./content-render.mjs";
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Pack a value into bytes {@link unpackValue} restores as an equal copy:
3
+ * Addresses, anchored links and Address maps keep their types, Maps, Sets,
4
+ * Dates and `undefined` survive, and a value shared by two places is still
5
+ * shared.
6
+ *
7
+ * @param {unknown} value - The value.
8
+ * @returns {Buffer} The bytes.
9
+ * @throws {Error} For a value holding a function, a symbol, or an object of
10
+ * another class.
11
+ */
12
+ export function packValue(value: unknown): Buffer;
13
+ /**
14
+ * Restore a value {@link packValue} packed. Every call returns a fresh copy.
15
+ *
16
+ * @param {Buffer|Uint8Array} bytes - The packed value.
17
+ * @returns {unknown} The value.
18
+ * @throws {Error} When the bytes are not a packed value.
19
+ */
20
+ export function unpackValue(bytes: Buffer | Uint8Array): unknown;
21
+ /**
22
+ * Where a package's note cache is kept: `build/cache/note-page/` under the
23
+ * package root.
24
+ *
25
+ * @param {{rootDir: string}} config - The resolved configuration.
26
+ * @returns {string} The directory.
27
+ */
28
+ export function noteCacheDirectory(config: {
29
+ rootDir: string;
30
+ }): string;
31
+ /**
32
+ * The toolchain a cached answer was derived with: package-build's version and
33
+ * the bytes of its own modules, the Node.js version, and the DuckDB version
34
+ * the query answers came from.
35
+ *
36
+ * @returns {string} Its hash.
37
+ */
38
+ export function toolchainFingerprint(): string;
39
+ /**
40
+ * What a note of a package renders against.
41
+ *
42
+ * @typedef {object} NoteTree
43
+ * @property {object[]} records - The tree's index records, as
44
+ * {@link module:engine/content-index.indexRecordsFor} derives them.
45
+ * @property {{index: Map<string, object>}} foreign - The dependencies'
46
+ * indexes, as {@link module:engine/metadata-index.loadForeignIndexes}
47
+ * returns them: the very ones the records were derived with.
48
+ * @property {(files: Iterable<string>, own?: string) => Promise<{sqlTables: Map<string, object[]>, tables: "hit"|"miss"|"off"}>} answers -
49
+ * The query answers and generated sections of the notes named, by absolute
50
+ * path, as {@link module:engine/sql-tables.prepareTreeSqlTables} prepares
51
+ * them; `tables` is `"hit"` when every one was read from the store. `own`
52
+ * is the note being rendered, whose answers are always whole; every other
53
+ * note named may be answered with the view a linking note needs. Rejects
54
+ * with {@link NoteInputsChanged}, storing nothing, when the configuration,
55
+ * fonts or dependency indexes are not the ones the records were derived
56
+ * from, before or after the preparation.
57
+ * @property {(file: string) => string} textOf - A note's text, as the records
58
+ * were derived from it.
59
+ * @property {{tree: "hit"|"miss"|"off", notes: number}} cache - What the
60
+ * cache answered: the tree's records whole or not, and how many notes
61
+ * derived their own records.
62
+ */
63
+ /**
64
+ * The content tree's records, and its notes' query answers on demand, from
65
+ * the cache under `build/` while every input they were derived from is
66
+ * unchanged, and derived and stored otherwise.
67
+ *
68
+ * The dependencies' indexes are read here, between two readings of every
69
+ * input the store keys by: when the two differ, something changed while it
70
+ * was read, and the whole reading is done again, so what is stored is always
71
+ * derived from the bytes its key names.
72
+ *
73
+ * @param {object} config - The resolved configuration.
74
+ * @param {object} [options]
75
+ * @param {boolean} [options.cache=true] - Whether to read and write the
76
+ * cache; `false` derives everything and touches nothing.
77
+ * @param {string} [options.toolchain] - The toolchain's fingerprint;
78
+ * {@link toolchainFingerprint} by default.
79
+ * @param {typeof import("./sql-tables.mjs").prepareTreeSqlTables} [options.prepareTables] -
80
+ * Prepares query answers; {@link module:engine/sql-tables.prepareTreeSqlTables}
81
+ * by default.
82
+ * @param {typeof loadForeignIndexes} [options.loadForeign] - Reads the
83
+ * dependencies' indexes; {@link module:engine/metadata-index.loadForeignIndexes}
84
+ * by default.
85
+ * @returns {Promise<NoteTree>} The records, the indexes, the answers and what
86
+ * the cache did.
87
+ * @throws {Error} As the derivation does: a note that cannot be indexed, a
88
+ * dependency that has not been fetched or cannot be read, naming
89
+ * `package-build deps fetch`; and when the inputs keep changing while they
90
+ * are read.
91
+ */
92
+ export function noteTree(config: object, { cache, toolchain, loadForeign, prepareTables, }?: {
93
+ cache?: boolean | undefined;
94
+ toolchain?: string | undefined;
95
+ prepareTables?: typeof import("./sql-tables.mjs").prepareTreeSqlTables | undefined;
96
+ loadForeign?: typeof loadForeignIndexes | undefined;
97
+ }): Promise<NoteTree>;
98
+ /** The error a reading raises when its inputs moved while it was read. */
99
+ export class NoteInputsChanged extends Error {
100
+ }
101
+ /** How often a reading is done again when its inputs move under it. */
102
+ export const READ_ATTEMPTS: 3;
103
+ /**
104
+ * What a note of a package renders against.
105
+ */
106
+ export type NoteTree = {
107
+ /**
108
+ * - The tree's index records, as
109
+ * {@link module :engine/content-index.indexRecordsFor} derives them.
110
+ */
111
+ records: object[];
112
+ /**
113
+ * - The dependencies'
114
+ * indexes, as {@link module :engine/metadata-index.loadForeignIndexes}returns them: the very ones the records were derived with.
115
+ */
116
+ foreign: {
117
+ index: Map<string, object>;
118
+ };
119
+ /**
120
+ * -
121
+ * The query answers and generated sections of the notes named, by absolute
122
+ * path, as {@link module :engine/sql-tables.prepareTreeSqlTables} prepares
123
+ * them; `tables` is `"hit"` when every one was read from the store. `own`
124
+ * is the note being rendered, whose answers are always whole; every other
125
+ * note named may be answered with the view a linking note needs. Rejects
126
+ * with {@link NoteInputsChanged}, storing nothing, when the configuration,
127
+ * fonts or dependency indexes are not the ones the records were derived
128
+ * from, before or after the preparation.
129
+ */
130
+ answers: (files: Iterable<string>, own?: string) => Promise<{
131
+ sqlTables: Map<string, object[]>;
132
+ tables: "hit" | "miss" | "off";
133
+ }>;
134
+ /**
135
+ * - A note's text, as the records
136
+ * were derived from it.
137
+ */
138
+ textOf: (file: string) => string;
139
+ /**
140
+ * - What the
141
+ * cache answered: the tree's records whole or not, and how many notes
142
+ * derived their own records.
143
+ */
144
+ cache: {
145
+ tree: "hit" | "miss" | "off";
146
+ notes: number;
147
+ };
148
+ };
149
+ import { loadForeignIndexes } from "./metadata-index.mjs";
@@ -0,0 +1,92 @@
1
+ /**
2
+ * A rendered note.
3
+ *
4
+ * @typedef {object} NotePage
5
+ * @property {string} html - The page body, as the site theme receives it,
6
+ * with every page it links counted as reachable from the homepage.
7
+ * @property {string} styles - The CSS the theme applies to a page body; see
8
+ * {@link module:engine/content-styles.noteBodyStylesheet}.
9
+ * @property {Array<{file?: string, line?: number, column?: number,
10
+ * severity: string, message: string}>} findings - What the render found,
11
+ * each printing as `file:line:column: severity: message` through
12
+ * `formatDiagnostic`.
13
+ * @property {{tree: "hit"|"miss"|"off", notes: number, tables: "hit"|"miss"|"off"}} cache -
14
+ * What the cache answered: `tree` whether the content tree's records were
15
+ * reused whole, `notes` how many notes derived their own records again, and
16
+ * `tables` whether the query answers of the note and of the notes it links
17
+ * were all reused.
18
+ */
19
+ /**
20
+ * Render one note of a repository to the body HTML of its site page.
21
+ *
22
+ * Loads the repository's `package-build.config.yaml`, its dependencies'
23
+ * fetched indexes, the content tree's records and the note's own query
24
+ * answers, and renders the note against them as `package-build site build`
25
+ * renders its page. The records and answers come from
26
+ * `build/cache/note-page/` while every input they were derived from is
27
+ * unchanged; see {@link module:engine/note-cache.noteTree}. The note is read
28
+ * from disk, so what is rendered is what is saved.
29
+ *
30
+ * @param {string} file - The note, relative to `root` or absolute.
31
+ * @param {object} [options]
32
+ * @param {string} [options.root] - The repository, or a directory inside it;
33
+ * the working directory by default.
34
+ * @param {boolean} [options.cache=true] - Whether to read and write the cache;
35
+ * `false` derives everything and writes nothing.
36
+ * @param {typeof import("./sql-tables.mjs").prepareTreeSqlTables} [options.prepareTables] -
37
+ * Prepares the query answers;
38
+ * {@link module:engine/sql-tables.prepareTreeSqlTables} by default. A host
39
+ * may wrap it, to time or observe the preparation.
40
+ * @returns {Promise<NotePage>} The body, its styles, the findings and what the
41
+ * cache did.
42
+ * @throws {Error} When there is no configuration; when the file is not a
43
+ * note of the content tree; when a dependency's index has not been fetched
44
+ * or cannot be read, naming `package-build deps fetch`; when a note of the
45
+ * tree cannot be indexed, as the content-index build reports it; and when
46
+ * the configuration, fonts or dependency indexes keep changing while they
47
+ * are read.
48
+ */
49
+ export function renderNoteFile(file: string, { root, cache, prepareTables }?: {
50
+ root?: string | undefined;
51
+ cache?: boolean | undefined;
52
+ prepareTables?: typeof import("./sql-tables.mjs").prepareTreeSqlTables | undefined;
53
+ }): Promise<NotePage>;
54
+ /**
55
+ * A rendered note.
56
+ */
57
+ export type NotePage = {
58
+ /**
59
+ * - The page body, as the site theme receives it,
60
+ * with every page it links counted as reachable from the homepage.
61
+ */
62
+ html: string;
63
+ /**
64
+ * - The CSS the theme applies to a page body; see
65
+ * {@link module :engine/content-styles.noteBodyStylesheet}.
66
+ */
67
+ styles: string;
68
+ /**
69
+ * - What the render found,
70
+ * each printing as `file:line:column: severity: message` through
71
+ * `formatDiagnostic`.
72
+ */
73
+ findings: Array<{
74
+ file?: string;
75
+ line?: number;
76
+ column?: number;
77
+ severity: string;
78
+ message: string;
79
+ }>;
80
+ /**
81
+ * -
82
+ * What the cache answered: `tree` whether the content tree's records were
83
+ * reused whole, `notes` how many notes derived their own records again, and
84
+ * `tables` whether the query answers of the note and of the notes it links
85
+ * were all reused.
86
+ */
87
+ cache: {
88
+ tree: "hit" | "miss" | "off";
89
+ notes: number;
90
+ tables: "hit" | "miss" | "off";
91
+ };
92
+ };
@@ -114,6 +114,36 @@ export function configFromData(data: unknown, configPath: string): import("../co
114
114
  * compilers know neither what to compile nor where to put it.
115
115
  */
116
116
  export function loadPackConfig(): import("../content-config.mjs").ContentBuildConfig;
117
+ /**
118
+ * The resolved, frozen configuration one file declares.
119
+ *
120
+ * {@link loadPackConfig} reads the file the working directory resolves to and
121
+ * keeps it; this reads the file named, every time, for a caller working on a
122
+ * repository other than the one it stands in.
123
+ *
124
+ * @param {string} configPath - Absolute path of the configuration file.
125
+ * @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
126
+ * @throws {Error} As {@link loadPackConfig}, when the file cannot be loaded.
127
+ */
128
+ export function readPackConfig(configPath: string): import("../content-config.mjs").ContentBuildConfig;
129
+ /**
130
+ * Run `fn` with `config` as the configuration {@link loadPackConfig} answers.
131
+ *
132
+ * The engine reads configured values through {@link loadPackConfig} wherever
133
+ * a caller passes no configuration. A caller working on a repository other
134
+ * than the one it stands in makes those reads answer with that repository's
135
+ * configuration for the whole of `fn`, its asynchronous continuations
136
+ * included, and for nothing else running beside it.
137
+ *
138
+ * @template T
139
+ * @param {import("../content-config.mjs").ContentBuildConfig} config - The
140
+ * resolved configuration.
141
+ * @param {() => T} fn - The work, synchronous or asynchronous.
142
+ * @param {string} [file] - The file `config` was read from, which
143
+ * {@link packConfigPath} answers within `fn`.
144
+ * @returns {T} What `fn` returned.
145
+ */
146
+ export function withPackConfig<T>(config: import("../content-config.mjs").ContentBuildConfig, fn: () => T, file?: string): T;
117
147
  /**
118
148
  * The file {@link loadPackConfig} resolved the configuration from.
119
149
  *
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The `only` a query preparation takes: one note file, several, or none.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Whether a note file falls outside a preparation's `only`.
8
+ *
9
+ * @param {string|Iterable<string>|undefined} only - One note file, several,
10
+ * or none, which keeps every note.
11
+ * @param {string} file - The note file.
12
+ * @returns {boolean} Whether `only` is given and leaves `file` out.
13
+ */
14
+ export function outsideOnly(only: string | Iterable<string> | undefined, file: string): boolean;
@@ -13,6 +13,41 @@
13
13
  * expressions and figure numbers resolved. A query, which a buffer cannot
14
14
  * answer, is left as written.
15
15
  */
16
+ /**
17
+ * What a page of the package resolves against, from its content-index records.
18
+ *
19
+ * The site build reads the same answers from its whole tree; this derives
20
+ * them from the records alone, so a single note renders as its page does
21
+ * without the tree. Each note that publishes a page is an entry at the URL
22
+ * the site gives it: the homepage at the package root, a note
23
+ * `site.sections` names at its section, and every other at its address.
24
+ *
25
+ * @param {object} options
26
+ * @param {object} options.config - The resolved package configuration.
27
+ * @param {object[]} options.records - The content index's records.
28
+ * @param {object} [options.foreign] - The dependencies' indexes, as
29
+ * {@link module:engine/metadata-index.loadForeignIndexes} returns them.
30
+ * @param {(record: object) => object[]|undefined} [options.anchorsOf] - A
31
+ * note's anchors as its page emits them, where they differ from the
32
+ * record's: those of its body with its query answers and generated sections
33
+ * expanded. Absent, or answering nothing, the record's own.
34
+ * @returns {{site: object, assets: object, events: Map<string, object>, runs: object,
35
+ * pages: Map<string, object>}} The link index, the asset address space,
36
+ * every note's events, the package's marked-text context, and the record
37
+ * behind each page by its URL.
38
+ */
39
+ export function recordSiteContext({ config, records, foreign, anchorsOf }: {
40
+ config: object;
41
+ records: object[];
42
+ foreign?: object | undefined;
43
+ anchorsOf?: ((record: object) => object[] | undefined) | undefined;
44
+ }): {
45
+ site: object;
46
+ assets: object;
47
+ events: Map<string, object>;
48
+ runs: object;
49
+ pages: Map<string, object>;
50
+ };
16
51
  /**
17
52
  * Build the object a preview with an index renders from.
18
53
  *
@@ -146,93 +146,6 @@ export function siteBuildFailed(result: object): boolean;
146
146
  * @returns {Map<string, object[]>} Package → the notes it may tabulate.
147
147
  */
148
148
  export function tableUniverse(pages: object[]): Map<string, object[]>;
149
- /**
150
- * The frontmatter a page publishes with.
151
- *
152
- * A content page carries the package the build **derived**. No note
153
- * declares one, so the note's frontmatter alone would publish a page that does
154
- * not say which package it belongs to. Writing the derived value keeps a page
155
- * self-describing and makes sweeping the field out of a content tree
156
- * output-preserving for a site as it already is for the packs.
157
- *
158
- * **`related` is derived, never authored.** What links to a page is a fact
159
- * about every other page, known only once the whole tree has resolved — see
160
- * {@link module:engine/related-pages} — so an authored value is dropped, and
161
- * {@link renderPages} sets the derived block once it holds the graph.
162
- *
163
- * @param {object} page - The page.
164
- * @param {object} options
165
- * @param {(data: object, page: object) => void} [options.decorate] - Called
166
- * with each page's frontmatter, for whatever a consumer's own pass adds.
167
- * @param {(value: unknown, type: string, accepts?: Iterable<string>) => string|null} [options.artSrc] -
168
- * Translates an art address into the pathname the website resolver takes, or
169
- * `null` where nothing answers it.
170
- * @param {(src: string) => string} [options.webSrc] - Translates an authored
171
- * pathname into the address the website serves. Every artwork field goes
172
- * through it, so a page's `img:` and its body images name the same file the
173
- * same way.
174
- * @returns {object} The frontmatter.
175
- */
176
- export function pageFrontmatter(page: object, { decorate, webSrc, artSrc }: {
177
- decorate?: ((data: object, page: object) => void) | undefined;
178
- artSrc?: ((value: unknown, type: string, accepts?: Iterable<string>) => string | null) | undefined;
179
- webSrc?: ((src: string) => string) | undefined;
180
- }): object;
181
- /** Transform one content page. */
182
- export function renderSitePage(page: any, { index, foreign, pass, decorate, sqlTables, config, artIndex, figuresByAddress, events, drawings, runs, }: {
183
- index: any;
184
- foreign: any;
185
- pass?: {} | undefined;
186
- decorate: any;
187
- sqlTables: any;
188
- config: any;
189
- artIndex: any;
190
- figuresByAddress?: Map<any, any> | undefined;
191
- events: any;
192
- drawings?: Map<any, any> | undefined;
193
- runs?: import("./content-scripts.mjs").RunContext | undefined;
194
- }): {
195
- page: any;
196
- markdown: string;
197
- figures: object[];
198
- data: object;
199
- resolved: object[];
200
- tableErrors: object[];
201
- expressionErrors: object[];
202
- secretErrors: {
203
- file: any;
204
- line: number;
205
- column: number;
206
- message: string;
207
- }[];
208
- captionErrors: {
209
- file: any;
210
- line: any;
211
- column: any;
212
- message: any;
213
- }[];
214
- headingErrors: {
215
- line: number;
216
- column: number;
217
- message: string;
218
- file: any;
219
- }[];
220
- footnoteErrors: {
221
- file: any;
222
- line: number;
223
- column: number;
224
- message: string;
225
- }[];
226
- wikiErrors: object[];
227
- imageErrors: any[];
228
- embedErrors: {
229
- file: string;
230
- line: number;
231
- column: number | undefined;
232
- severity: "error";
233
- message: string;
234
- }[];
235
- };
236
149
  /**
237
150
  * Renders every page.
238
151
  *
@@ -283,18 +196,6 @@ export function renderPages(pages: object[], options: object): {
283
196
  pages: object[];
284
197
  runs: object;
285
198
  };
286
- /**
287
- * Resolves `site.pass` to its bundle.
288
- *
289
- * @param {string|undefined} name - The configured name.
290
- * @param {object} options - The configured options, plus `repoRoot`.
291
- * @returns {{beforeLinks?: Function}} The bundle.
292
- */
293
- export function resolveSitePass(name: string | undefined, options: object): {
294
- beforeLinks?: Function;
295
- };
296
- /** The site frontmatter decoration shared by the site writer and page preview. */
297
- export function sitePageDecorator(config: any, index: any): (data: any, page: any) => void;
298
199
  /**
299
200
  * Collects, checks and renders a content tree's pages, and reports what it found.
300
201
  *
@@ -338,5 +239,9 @@ export function buildSite({ config, sqlTables, progress }?: {
338
239
  manifests: object | null;
339
240
  site?: object;
340
241
  };
341
- export { formatUnaddressableFinding };
242
+ import { pageFrontmatter } from "./site-page.mjs";
243
+ import { renderSitePage } from "./site-page.mjs";
244
+ import { resolveSitePass } from "./site-page.mjs";
245
+ import { sitePageDecorator } from "./site-page.mjs";
342
246
  import { formatUnaddressableFinding } from "./metadata-index.mjs";
247
+ export { pageFrontmatter, renderSitePage, resolveSitePass, sitePageDecorator, formatUnaddressableFinding };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Check a navigation's shape, and return it.
3
3
  *
4
- * `[{name, url, children?: [{name, url}]}]`, every `url` absolute. Checked
4
+ * `[{name, url, children?: [{name, url}]}]`, every `url` a non-empty string. Checked
5
5
  * on fetch and again on read: a file that is not a navigation would otherwise
6
6
  * reach the menu as `undefined` labels and links.
7
7
  *
@@ -19,7 +19,7 @@ export function checkNavigation(value: unknown, where?: string): NavigationEntry
19
19
  */
20
20
  export function navigationCacheDir(config: object): string;
21
21
  /**
22
- * The cached navigation.
22
+ * The cached navigation, with canonical owned links on the effective homepage origin.
23
23
  *
24
24
  * **Reads the cache only.** A cold cache is an error naming the command that
25
25
  * fills it, rather than a download nobody asked for: a site build that reaches
@@ -93,6 +93,7 @@ export const BRAND: Readonly<{
93
93
  }>;
94
94
  /** Where the navigation is published. */
95
95
  export const NAVIGATION_URL: "https://www.heroiclands.org/nav.json";
96
+ export { CANONICAL_CDN_ORIGIN } from "./site-origins.mjs";
96
97
  /** The cached navigation's file name. */
97
98
  export const NAVIGATION_FILE: "nav.json";
98
99
  /**
@@ -33,16 +33,22 @@ export function inlineSvg(svg: string): string;
33
33
  * @param {object} opts.config - The resolved configuration.
34
34
  * @param {string} opts.base - The site base every page is served under,
35
35
  * ending in a slash: what every name in a drawing links through.
36
+ * @param {Iterable<string>} [opts.only] - The places to draw, by shortcode,
37
+ * of those given a map; absent, every one.
38
+ * @param {string} [opts.outDir] - Where to draw, emptied first;
39
+ * {@link SITE_MAP_DIR} under the package root by default.
36
40
  * @returns {{maps: Map<string, SiteMap>, findings: Array<{file: string,
37
41
  * line?: number, column?: number, severity: "error"|"warning",
38
42
  * message: string}>}} The drawings, keyed by the file name a **From here**
39
43
  * section names, and warnings for relations that name no place.
40
44
  */
41
- export function drawSiteMaps({ records, foreignIndex, config, base }: {
45
+ export function drawSiteMaps({ records, foreignIndex, config, base, only, outDir, }: {
42
46
  records: Array<Record<string, any>>;
43
47
  foreignIndex: Map<string, object>;
44
48
  config: object;
45
49
  base: string;
50
+ only?: Iterable<string> | undefined;
51
+ outDir?: string | undefined;
46
52
  }): {
47
53
  maps: Map<string, SiteMap>;
48
54
  findings: Array<{
@@ -53,16 +59,6 @@ export function drawSiteMaps({ records, foreignIndex, config, base }: {
53
59
  message: string;
54
60
  }>;
55
61
  };
56
- /**
57
- * The figure the website sets a drawing in: the SVG inline, so its place names
58
- * stay links, with no blank line inside it — a blank line would end the HTML
59
- * block and hand the rest of the drawing to the Markdown renderer.
60
- *
61
- * @param {string} svg - The drawing, as {@link inlineSvg} wrote it.
62
- * @param {string} alt - What the drawing shows, for a screen reader.
63
- * @returns {string} One HTML block.
64
- */
65
- export function inlineDrawingFigure(svg: string, alt: string): string;
66
62
  /**
67
63
  * Where the site build draws, below the package root.
68
64
  *
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The owned CDN origin, re-exported by site-config beside the chrome addresses.
3
+ * Kept in a leaf because site-config reaches pathnames through configuration and
4
+ * content rendering; importing site-config from pathnames would close that cycle.
5
+ *
6
+ * @module
7
+ */
8
+ /** The canonical public origin whose asset URLs follow the selected build host. */
9
+ export const CANONICAL_CDN_ORIGIN: "https://cdn.heroiclands.org";