@heroiclands/package-build 23.12.8 → 24.0.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 (258) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +3 -2
  3. package/assets/preview/gitea.ini +12 -7
  4. package/assets/styles/content-base.css +293 -24
  5. package/bin/content-commands.mjs +370 -213
  6. package/bin/package-build-preview.mjs +2 -2
  7. package/bin/package-build.mjs +3 -2
  8. package/ci/check-banners.mjs +7 -7
  9. package/config.mjs +21 -6
  10. package/content-config.mjs +199 -80
  11. package/docs/api.md +110 -89
  12. package/docs/authoring/links-and-markup.md +469 -97
  13. package/docs/commands.md +259 -78
  14. package/docs/configuration.md +211 -103
  15. package/docs/content-format.md +4 -4
  16. package/docs/diagnostics.md +44 -2
  17. package/docs/getting-started.md +1 -1
  18. package/docs/guides/icons.md +1 -1
  19. package/docs/guides/module-project.md +5 -8
  20. package/docs/guides/release-and-deploy.md +2 -2
  21. package/docs/guides/site.md +112 -23
  22. package/docs/guides/system-project.md +1 -1
  23. package/docs/guides/writing-in-invented-scripts.md +1247 -0
  24. package/docs/index.md +1 -0
  25. package/docs/project-setup.md +28 -30
  26. package/docs/reference/format-details.md +677 -319
  27. package/docs/reference/note-types.md +47 -10
  28. package/docs/reference/theme-pages.md +178 -108
  29. package/docs/source-preview.md +16 -2
  30. package/engine/anchors.mjs +2 -2
  31. package/engine/asset-shipping.mjs +22 -10
  32. package/engine/base-compiler.mjs +62 -104
  33. package/engine/changelog-lint.mjs +1 -1
  34. package/engine/code-fences.mjs +3 -3
  35. package/engine/content-address.mjs +1 -1
  36. package/engine/content-alerts.mjs +8 -4
  37. package/engine/content-blocks.mjs +29 -14
  38. package/engine/content-embeds.mjs +1 -1
  39. package/engine/content-expand.mjs +376 -0
  40. package/engine/content-figures.mjs +18 -11
  41. package/engine/content-format.yaml +8 -0
  42. package/engine/content-glyphs.mjs +213 -0
  43. package/engine/content-images.mjs +6 -7
  44. package/engine/content-index.mjs +1 -1
  45. package/engine/content-links.mjs +3 -3
  46. package/engine/content-lint.mjs +1 -1
  47. package/engine/content-render.mjs +453 -0
  48. package/engine/content-scripts.mjs +376 -43
  49. package/engine/content-spans.mjs +6 -3
  50. package/engine/content-styles.mjs +91 -0
  51. package/engine/content-symbols.mjs +186 -0
  52. package/engine/content-verse.mjs +738 -0
  53. package/engine/core-report.mjs +169 -0
  54. package/engine/core-vocabulary.mjs +208 -0
  55. package/engine/dictionary-html.mjs +119 -0
  56. package/engine/dictionary-rows.mjs +15 -4
  57. package/engine/field-reference.mjs +5 -5
  58. package/engine/first-use.mjs +17 -27
  59. package/engine/frontmatter-lint.mjs +1 -1
  60. package/engine/heading-attributes.mjs +89 -0
  61. package/engine/helpers.mjs +61 -101
  62. package/engine/homepage.mjs +17 -29
  63. package/engine/html-minify.mjs +70 -0
  64. package/engine/index.mjs +8 -5
  65. package/engine/key-case-collisions.mjs +136 -0
  66. package/engine/language-data.mjs +203 -6
  67. package/engine/language-rows.mjs +8 -2
  68. package/engine/language-terms.mjs +186 -16
  69. package/engine/language-words.mjs +12 -0
  70. package/engine/logographic.mjs +255 -0
  71. package/engine/map-lexicon.mjs +262 -47
  72. package/engine/map-places.mjs +5 -4
  73. package/engine/markdown-expressions.mjs +0 -2
  74. package/engine/markdownlint-run.mjs +9 -3
  75. package/engine/markup-validation.mjs +21 -0
  76. package/engine/metre-declaration.mjs +134 -0
  77. package/engine/names.mjs +36 -9
  78. package/engine/note-matter.mjs +66 -0
  79. package/engine/note-vocabulary.mjs +187 -3
  80. package/engine/pdf-build.mjs +69 -133
  81. package/engine/pdf-render.mjs +205 -58
  82. package/engine/post-fields.mjs +157 -0
  83. package/engine/preview-index.mjs +130 -0
  84. package/engine/preview-source.mjs +450 -0
  85. package/engine/project-init.mjs +5 -12
  86. package/engine/prose-config.mjs +3 -3
  87. package/engine/read-stdin.mjs +29 -0
  88. package/engine/run-rows.mjs +2 -2
  89. package/engine/run-words.mjs +6 -112
  90. package/engine/script-emphasis-terms.mjs +38 -0
  91. package/engine/script-emphasis.mjs +295 -0
  92. package/engine/script-font-delivery.mjs +4 -4
  93. package/engine/site-build.mjs +260 -315
  94. package/engine/site-config.mjs +26 -238
  95. package/engine/site-files.mjs +175 -0
  96. package/engine/site-index.mjs +9 -4
  97. package/engine/site-preview.mjs +52 -525
  98. package/engine/site-render.mjs +593 -0
  99. package/engine/site-root.mjs +22 -13
  100. package/engine/site-search.mjs +161 -0
  101. package/engine/site-serve.mjs +273 -0
  102. package/engine/site-theme-banner.mjs +154 -0
  103. package/engine/site-theme-urls.mjs +125 -0
  104. package/engine/site-theme.mjs +926 -0
  105. package/engine/sql-tables.mjs +20 -3
  106. package/engine/text-maps.mjs +57 -10
  107. package/engine/typographer.mjs +248 -0
  108. package/engine/verse-metre.mjs +357 -0
  109. package/engine/wikilink-syntax.mjs +29 -47
  110. package/engine/wikilinks.mjs +758 -279
  111. package/engine/word-forms.mjs +364 -0
  112. package/engine/word-rows.mjs +12 -2
  113. package/engine/word-terms.mjs +38 -4
  114. package/manifest.mjs +3 -4
  115. package/package.json +11 -8
  116. package/{hugo-theme → site-theme}/data/banners.yaml +6 -10
  117. package/site-theme/layouts/base.hbs +31 -0
  118. package/site-theme/layouts/blog-list.hbs +30 -0
  119. package/site-theme/layouts/blog-post.hbs +40 -0
  120. package/site-theme/layouts/home.hbs +35 -0
  121. package/site-theme/layouts/list.hbs +20 -0
  122. package/site-theme/layouts/not-found.hbs +31 -0
  123. package/site-theme/layouts/page.hbs +7 -0
  124. package/site-theme/partials/copy-buttons.hbs +37 -0
  125. package/site-theme/partials/draft-notice.hbs +5 -0
  126. package/site-theme/partials/exturl.hbs +2 -0
  127. package/{hugo-theme/layouts/partials/footer.html → site-theme/partials/footer.hbs} +10 -7
  128. package/site-theme/partials/header.hbs +44 -0
  129. package/site-theme/partials/hero-banner.hbs +11 -0
  130. package/site-theme/partials/infobox-entry.hbs +2 -0
  131. package/site-theme/partials/infobox-section.hbs +31 -0
  132. package/site-theme/partials/infobox-value.hbs +2 -0
  133. package/site-theme/partials/infobox.hbs +18 -0
  134. package/site-theme/partials/list-members.hbs +9 -0
  135. package/site-theme/partials/nav-links.hbs +5 -0
  136. package/site-theme/partials/page-body.hbs +48 -0
  137. package/site-theme/partials/page-list.hbs +14 -0
  138. package/site-theme/partials/related.hbs +24 -0
  139. package/site-theme/partials/search-meta.hbs +6 -0
  140. package/site-theme/partials/search-names.hbs +2 -0
  141. package/site-theme/partials/search.hbs +52 -0
  142. package/site-theme/partials/tags.hbs +4 -0
  143. package/site-theme/partials/toc.hbs +7 -0
  144. package/{hugo-theme → site-theme}/static/css/style.css +106 -14
  145. package/stage.mjs +1 -2
  146. package/types/config.d.mts +8 -4
  147. package/types/content-config.d.mts +15 -15
  148. package/types/engine/asset-shipping.d.mts +5 -2
  149. package/types/engine/base-compiler.d.mts +20 -2
  150. package/types/engine/code-fences.d.mts +3 -3
  151. package/types/engine/content-address.d.mts +1 -1
  152. package/types/engine/content-alerts.d.mts +8 -2
  153. package/types/engine/content-blocks.d.mts +11 -4
  154. package/types/engine/content-expand.d.mts +288 -0
  155. package/types/engine/content-figures.d.mts +11 -3
  156. package/types/engine/content-glyphs.d.mts +85 -0
  157. package/types/engine/content-images.d.mts +6 -7
  158. package/types/engine/content-links.d.mts +1 -1
  159. package/types/engine/content-render.d.mts +199 -0
  160. package/types/engine/content-scripts.d.mts +70 -4
  161. package/types/engine/content-spans.d.mts +1 -1
  162. package/types/engine/content-styles.d.mts +33 -0
  163. package/types/engine/content-symbols.d.mts +35 -0
  164. package/types/engine/content-verse.d.mts +240 -0
  165. package/types/engine/core-report.d.mts +45 -0
  166. package/types/engine/core-vocabulary.d.mts +53 -0
  167. package/types/engine/dictionary-html.d.mts +8 -0
  168. package/types/engine/dictionary-rows.d.mts +10 -0
  169. package/types/engine/field-reference.d.mts +2 -2
  170. package/types/engine/heading-attributes.d.mts +20 -0
  171. package/types/engine/helpers.d.mts +3 -10
  172. package/types/engine/homepage.d.mts +11 -24
  173. package/types/engine/html-minify.d.mts +9 -0
  174. package/types/engine/index.d.mts +2 -1
  175. package/types/engine/key-case-collisions.d.mts +28 -0
  176. package/types/engine/language-data.d.mts +0 -2
  177. package/types/engine/language-terms.d.mts +24 -2
  178. package/types/engine/logographic.d.mts +45 -0
  179. package/types/engine/map-lexicon.d.mts +66 -13
  180. package/types/engine/map-places.d.mts +8 -0
  181. package/types/engine/markdown-expressions.d.mts +0 -2
  182. package/types/engine/metre-declaration.d.mts +39 -0
  183. package/types/engine/names.d.mts +34 -3
  184. package/types/engine/note-matter.d.mts +48 -0
  185. package/types/engine/pdf-render.d.mts +6 -2
  186. package/types/engine/post-fields.d.mts +44 -0
  187. package/types/engine/preview-index.d.mts +72 -0
  188. package/types/engine/preview-source.d.mts +51 -0
  189. package/types/engine/read-stdin.d.mts +11 -0
  190. package/types/engine/run-rows.d.mts +2 -2
  191. package/types/engine/run-words.d.mts +0 -15
  192. package/types/engine/script-emphasis-terms.d.mts +21 -0
  193. package/types/engine/script-emphasis.d.mts +84 -0
  194. package/types/engine/script-font-delivery.d.mts +4 -4
  195. package/types/engine/site-build.d.mts +71 -113
  196. package/types/engine/site-config.d.mts +14 -134
  197. package/types/engine/site-files.d.mts +73 -0
  198. package/types/engine/site-index.d.mts +11 -3
  199. package/types/engine/site-preview.d.mts +15 -19
  200. package/types/engine/site-render.d.mts +171 -0
  201. package/types/engine/site-root.d.mts +12 -4
  202. package/types/engine/site-search.d.mts +54 -0
  203. package/types/engine/site-serve.d.mts +44 -0
  204. package/types/engine/site-theme-banner.d.mts +51 -0
  205. package/types/engine/site-theme-urls.d.mts +60 -0
  206. package/types/engine/site-theme.d.mts +127 -0
  207. package/types/engine/sql-tables.d.mts +4 -1
  208. package/types/engine/text-maps.d.mts +17 -3
  209. package/types/engine/typographer.d.mts +11 -0
  210. package/types/engine/verse-metre.d.mts +131 -0
  211. package/types/engine/wikilink-syntax.d.mts +30 -48
  212. package/types/engine/wikilinks.d.mts +330 -14
  213. package/types/engine/word-forms.d.mts +83 -0
  214. package/types/manifest.d.mts +3 -4
  215. package/types/stage.d.mts +1 -2
  216. package/types/vendor/markdownlint.d.mts +8 -0
  217. package/vendor/markdownlint.mjs +20388 -0
  218. package/engine/content-poetry.mjs +0 -240
  219. package/engine/web-wikilinks.mjs +0 -594
  220. package/hugo-theme/layouts/404.html +0 -85
  221. package/hugo-theme/layouts/_default/baseof.html +0 -67
  222. package/hugo-theme/layouts/_default/list.html +0 -139
  223. package/hugo-theme/layouts/_default/single.html +0 -4
  224. package/hugo-theme/layouts/blog/list.html +0 -54
  225. package/hugo-theme/layouts/blog-post/single.html +0 -36
  226. package/hugo-theme/layouts/index.html +0 -75
  227. package/hugo-theme/layouts/partials/banner-url.html +0 -141
  228. package/hugo-theme/layouts/partials/cdn-url.html +0 -36
  229. package/hugo-theme/layouts/partials/dictionary-lists.html +0 -58
  230. package/hugo-theme/layouts/partials/draft-notice.html +0 -41
  231. package/hugo-theme/layouts/partials/draft-state.html +0 -26
  232. package/hugo-theme/layouts/partials/header.html +0 -35
  233. package/hugo-theme/layouts/partials/hero-banner.html +0 -85
  234. package/hugo-theme/layouts/partials/infobox/entry.html +0 -18
  235. package/hugo-theme/layouts/partials/infobox/items.html +0 -26
  236. package/hugo-theme/layouts/partials/infobox/section.html +0 -56
  237. package/hugo-theme/layouts/partials/infobox/value.html +0 -43
  238. package/hugo-theme/layouts/partials/infobox.html +0 -57
  239. package/hugo-theme/layouts/partials/page-body.html +0 -152
  240. package/hugo-theme/layouts/partials/related.html +0 -138
  241. package/hugo-theme/layouts/partials/search.html +0 -84
  242. package/hugo-theme/layouts/partials/site-url.html +0 -32
  243. package/hugo-theme/layouts/partials/tags.html +0 -30
  244. package/hugo-theme/layouts/partials/toc.html +0 -28
  245. package/hugo-theme/layouts/partials/warn-missing-banner.html +0 -3
  246. package/hugo-theme/layouts/shortcodes/cartouche.html +0 -64
  247. package/hugo-theme/layouts/shortcodes/exturl.html +0 -12
  248. package/hugo-theme/layouts/shortcodes/glyph.html +0 -43
  249. package/hugo-theme/layouts/shortcodes/pagelist.html +0 -63
  250. package/hugo-theme/theme.toml +0 -11
  251. package/types/engine/content-poetry.d.mts +0 -58
  252. package/types/engine/web-wikilinks.d.mts +0 -107
  253. /package/{hugo-theme → site-theme}/data/banner-kinds.yaml +0 -0
  254. /package/{hugo-theme → site-theme}/static/css/sohl-icons.css +0 -0
  255. /package/{hugo-theme → site-theme}/static/fonts/game-icons-kb.woff2 +0 -0
  256. /package/{hugo-theme → site-theme}/static/img/banners/takheperu-hieroglyphs.svg +0 -0
  257. /package/{hugo-theme → site-theme}/static/img/cartouches/takheperu.svg +0 -0
  258. /package/{hugo-theme → site-theme}/static/js/inline-spoilers.js +0 -0
package/docs/api.md CHANGED
@@ -295,19 +295,18 @@ Schema fields a note may never author, because the document writes them during p
295
295
 
296
296
  The package homepage: a note that compiles to a page rather than a compendium document, hand-authored at the conventional shortcode `root`, checked for its address fields and for uniqueness across the tree.
297
297
 
298
- | Export | Signature | Returns | Use it when |
299
- | ---------------------------- | ------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------ |
300
- | `HOMEPAGE_TYPE` | `const HOMEPAGE_TYPE` | — | naming the note type that compiles to the package homepage |
301
- | `HOMEPAGE_FIELDS` | `const HOMEPAGE_FIELDS` | — | declaring what a homepage note may write under `sohl:` — nothing |
302
- | `HOMEPAGE_SHORTCODE` | `const HOMEPAGE_SHORTCODE` | — | naming the shortcode a package front page conventionally takes |
303
- | `HOMEPAGE_DESTINATION` | `const HOMEPAGE_DESTINATION` | — | naming the file a homepage is written to, `_index.md` at the site root |
304
- | `isHomepage` | `isHomepage(fm)` | `boolean` | checking whether a note's frontmatter declares the homepage type |
305
- | `HOMEPAGE_REFUSED_FIELDS` | `const HOMEPAGE_REFUSED_FIELDS` | — | naming the top-level fields a homepage refuses, and what each would decide |
306
- | `checkHomepageAddressFields` | `checkHomepageAddressFields(fm, ...)` | one finding per issue | checking what the address rule says about one homepage note's top-level fields |
307
- | `checkHomepageCount` | `checkHomepageCount(found, ...)` | one finding per offending note | requiring exactly one homepage note in a content tree |
308
- | `homepageTitle` | `homepageTitle(fm, config)` | `string` | resolving the title a homepage publishes under, defaulting to the package's manifest title |
309
- | `homepageFrontmatter` | `homepageFrontmatter(fm, ...)` | `object` | assembling the frontmatter a homepage publishes with, note plus derived values |
310
- | `homepageAddresses` | `homepageAddresses(body)` | `Array<{field, url, kind}>` | finding every address a homepage carries — the markdown links in its body |
298
+ | Export | Signature | Returns | Use it when |
299
+ | ---------------------------- | ------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
300
+ | `HOMEPAGE_TYPE` | `const HOMEPAGE_TYPE` | — | naming the note type that compiles to the package homepage |
301
+ | `HOMEPAGE_FIELDS` | `const HOMEPAGE_FIELDS` | — | declaring what a homepage note may write under `sohl:` — nothing |
302
+ | `HOMEPAGE_SHORTCODE` | `const HOMEPAGE_SHORTCODE` | — | naming the shortcode a package front page conventionally takes |
303
+ | `isHomepage` | `isHomepage(fm)` | `boolean` | checking whether a note's frontmatter declares the homepage type |
304
+ | `HOMEPAGE_REFUSED_FIELDS` | `const HOMEPAGE_REFUSED_FIELDS` | — | naming the top-level fields a homepage refuses, and what each would decide |
305
+ | `checkHomepageAddressFields` | `checkHomepageAddressFields(fm, ...)` | one finding per issue | checking what the address rule says about one homepage note's top-level fields |
306
+ | `checkHomepageCount` | `checkHomepageCount(found, ...)` | one finding per offending note | requiring exactly one homepage note in a content tree |
307
+ | `homepageTitle` | `homepageTitle(fm, config)` | `string` | resolving the title a homepage publishes under, defaulting to the package's manifest title, then to `site.title` |
308
+ | `homepageFrontmatter` | `homepageFrontmatter(fm, ...)` | `object` | assembling the frontmatter a homepage publishes with, note plus derived values |
309
+ | `homepageAddresses` | `homepageAddresses(body)` | `Array<{field, url, kind}>` | finding every address a homepage carries — the markdown links in its body |
311
310
 
312
311
  ### `engine.noteSchemas`
313
312
 
@@ -552,6 +551,7 @@ The places a map is drawn from — this package's index records and every fetche
552
551
  | `placesFromRecords` | `placesFromRecords(records, { contentBase, base })` | `MapWorld` | reading this package's places and polities out of its index records, with a page URL each where a site base is given |
553
552
  | `loadMapWorld` | `loadMapWorld({ config, contentBase, base, problems })` | `MapWorld` | loading the world a map is drawn from — this package's index and every declared dependency's |
554
553
  | `mapWorld` | `mapWorld({ records, foreignIndex, contentBase, base, config })` | `MapWorld` | the same world out of records and indexes a caller already holds, as the site build does |
554
+ | `placeShortcodes` | `placeShortcodes(value)` | `string[]` | reading a list of places — `parents`, a being's `homes`, a scenario's `locations` — as lower-case shortcodes |
555
555
  | `analyzeContainment` | `analyzeContainment(places, polities)` | `Containment` | finding the world, the continents, each place's continent and the anomalies of `parents` |
556
556
  | `subtreeOf` | `subtreeOf(analysis, root)` | `Set<string>` | every place beneath one, by following `parents` downward |
557
557
  | `containmentFindings` | `containmentFindings(analysis, places, { contentBase, config })` | `object[]` | the anomalies as `file:line:column: severity: message` findings — no parent, an unresolved parent and a cycle are errors |
@@ -615,7 +615,7 @@ The npm-installed Graphviz runtime draws SVG maps. The `.dot` source stays besid
615
615
 
616
616
  ### `engine.siteMaps`
617
617
 
618
- The map from each place, drawn for the site: `package-build site` draws the map from every place of this package given a **From here** section — with the site's base, so every name links through it — and sets each drawing inline in that section, where its place names stay links. `site.maps: false` gives no place the section, and nothing is drawn.
618
+ The map from each place, drawn for the site: `package-build site build` draws the map from every place of this package given a **From here** section — with the site's base, so every name links through it — and sets each drawing inline in that section, where its place names stay links. `site.maps: false` gives no place the section, and nothing is drawn.
619
619
 
620
620
  | Export | Signature | Returns | Use it when |
621
621
  | --------------------- | ------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------- |
@@ -723,7 +723,7 @@ resolved value: the culture a place or affiliation inherits through
723
723
 
724
724
  ### `engine.markdownExpressions`
725
725
 
726
- `renderMarkdownExpressions(body, { fm, dates, sqlResults, figures, file, bodyLine })` expands scalar frontmatter references and registered Handlebars helpers outside Markdown code. The `dateformat` helper uses `formatDateInCalendar`, in the calendar it names or else the note's resolved calendar, which it reads from `dates.noteCalendars` — the map a reckoning context built over content-index records carries; `gt`, `gte`, `lt`, `lte`, `eq`, `and`, `or`, and `not` evaluate explicit comparisons. The `sql` helper reads a prepared scalar result, which must contain one row and one column. The `ref` helper links to a captioned item by its anchor — `"#thorn"` on the same note — in `number` (the default, and admitted explicitly), `full` or `title` form; `figures` is a `Map` of this note's own figures, by id, to `{label, caption, hasCaption}`, and an address naming another note is reported as unresolved rather than looked up. `sqlQueriesInMarkdown` discovers SQL calls, including nested calls; `prepareInlineSqlExpressions` evaluates them through the same DuckDB connection as SQL tables. `EXPRESSION_HELPERS` is the one list of helpers: the engine registers from it and the authoring document is checked against it, so each name there carries the parameters it takes and a sentence on what it does. The renderer returns `{ markdown, findings }`, with source positions for invalid expressions. Site, Foundry and PDF passes invoke it after generated tables expand and before links resolve. Hugo shortcodes remain literal for the site renderer.
726
+ `renderMarkdownExpressions(body, { fm, dates, sqlResults, figures, file, bodyLine })` expands scalar frontmatter references and registered Handlebars helpers outside Markdown code. The `dateformat` helper uses `formatDateInCalendar`, in the calendar it names or else the note's resolved calendar, which it reads from `dates.noteCalendars` — the map a reckoning context built over content-index records carries; `gt`, `gte`, `lt`, `lte`, `eq`, `and`, `or`, and `not` evaluate explicit comparisons. The `sql` helper reads a prepared scalar result, which must contain one row and one column. The `ref` helper links to a captioned item by its anchor — `"#thorn"` on the same note — in `number` (the default, and admitted explicitly), `full` or `title` form; `figures` is a `Map` of this note's own figures, by id, to `{label, caption, hasCaption}`, and an address naming another note is reported as unresolved rather than looked up. `sqlQueriesInMarkdown` discovers SQL calls, including nested calls; `prepareInlineSqlExpressions` evaluates them through the same DuckDB connection as SQL tables. `EXPRESSION_HELPERS` is the one list of helpers: the engine registers from it and the authoring document is checked against it, so each name there carries the parameters it takes and a sentence on what it does. The renderer returns `{ markdown, findings }`, with source positions for invalid expressions. Site, Foundry and PDF passes invoke it after generated tables expand and before links resolve.
727
727
 
728
728
  | Export | Signature | Returns | Use it when |
729
729
  | --------------------------- | ------------------------------------------ | ---------------------- | ------------------------------------------------ |
@@ -1001,7 +1001,6 @@ into one warning for each occurrence; the message names the full name or
1001
1001
  explicit label that needs attention. Only `.name` spans and authored link
1002
1002
  labels enter name tracking; plain text is not searched. Indexed full names and
1003
1003
  declared aliases share an identity, while other labels use their whole text.
1004
- A whole bold indexed name receives an advisory without entering name tracking.
1005
1004
  Local and captured dependency names and aliases share the same corpus
1006
1005
  context. The command's lint target constructs
1007
1006
  the checker once, uses exact authored `raw` strings and keeps these findings
@@ -1295,33 +1294,56 @@ The toolchain's own content index — the files it ships, addressed. Every other
1295
1294
 
1296
1295
  Publishing a content tree as a website. `collectContentPages` gathers authored
1297
1296
  pages and link findings, while `collectHomepages` gathers package homepages.
1298
- `writeHomepages` places those homepages at each package's site root. The same
1297
+ `homepageData` gives each homepage's frontmatter and body, and `renderPages` renders the content pages. The same
1299
1298
  content tree can also compile into Foundry compendium packs.
1300
1299
 
1301
- | Export | Signature | Returns | Use it when |
1302
- | --------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
1303
- | `collectContentPages` | `function collectContentPages(contentBase, ctx)` | `{{pages: object[], addressFindings: object[], fmLinkFindings: object[]}}` | The content tree's pages, and what could not be addressed. |
1304
- | `collectHomepages` | `function collectHomepages(contentBase, ctx)` | `{{pages: object[], addressFindings: object[]}}` The homepage notes, in walk order, and the ones among them that could not be addressed. | The package's homepage notes — the authored page at `/<contentPackage>/`. |
1305
- | `writeHomepages` | `function writeHomepages(outRoot, pages, config)` | {number} How many pages were written. | Writes each homepage as the package root's `_index.md`. |
1306
- | `siteGates` | `siteGates(pages, findings, options)` | {object} The gate results and, when they pass, the built index. | The integrity gates a site build runs before it writes anything. |
1307
- | `emptyGates` | `function emptyGates()` | {object} An all-clear gate result. | The gate result of a build that ran none of them. |
1308
- | `gatesFailed` | `function gatesFailed(gates)` | — | Whether any gate produced a finding. |
1309
- | `tableUniverse` | `function tableUniverse(pages)` | {Map<string, object[]>} Package → the notes it may tabulate. | The universe a generated table searches, grouped by package. |
1310
- | `pageFrontmatter` | `function pageFrontmatter(page,` | {object} The frontmatter to write. | The frontmatter a page publishes with. |
1311
- | `pageDestination` | `function pageDestination(page)` | — | Where a page is written, relative to the output root: `<slug>.md`, flat under the mount. |
1312
- | `renderSitePage` | `renderSitePage(page, options)` | `{body, data, resolved, tableErrors, secretErrors, wikiErrors, imageErrors}` | Transform one page with the same passes the site writer uses. |
1313
- | `renderPages` | `function renderPages(pages, options)` | `{{written: number, byKind: Record<string, number>, tableErrors: object[], wikiErrors: object[]}}` | Renders and writes every page. |
1314
- | `resolveSitePass` | `function resolveSitePass(name, options)` | `{{beforeLinks?: Function}}` The bundle. | Resolves `site.pass` to its bundle. |
1315
- | `sitePageDecorator` | `sitePageDecorator(config, index)` | Function | Add the site's infobox and being metadata to a rendered page. |
1316
- | `buildSite` | `buildSite({ config, sqlTables })` | `{{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], mapFindings: object[], manifests: object\|null}}` | Builds a Hugo content tree from a content tree, and reports what it found. |
1300
+ | Export | Signature | Returns | Use it when |
1301
+ | --------------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
1302
+ | `collectContentPages` | `function collectContentPages(contentBase, ctx)` | `{{pages: object[], addressFindings: object[], fmLinkFindings: object[]}}` | The content tree's pages, and what could not be addressed. |
1303
+ | `collectHomepages` | `function collectHomepages(contentBase, ctx)` | `{{pages: object[], addressFindings: object[]}}` The homepage notes, in walk order, and the ones among them that could not be addressed. | The package's homepage notes — the authored page at `/<contentPackage>/`. |
1304
+ | `homepageData` | `homepageData(pages, config, options)` | `Array<{file, body, data}>` | Each homepage's frontmatter as its page publishes it, and its body. |
1305
+ | `siteGates` | `siteGates(pages, findings, options)` | {object} The gate results and, when they pass, the built index. | The integrity gates a site build runs before it writes anything. |
1306
+ | `emptyGates` | `function emptyGates()` | {object} An all-clear gate result. | The gate result of a build that ran none of them. |
1307
+ | `gatesFailed` | `function gatesFailed(gates)` | — | Whether any gate produced a finding. |
1308
+ | `siteBuildFailed` | `siteBuildFailed(result)` | `boolean` | Whether a gate fired or a note is in error, so the site is not to be deployed. |
1309
+ | `tableUniverse` | `function tableUniverse(pages)` | {Map<string, object[]>} Package → the notes it may tabulate. | The universe a generated table searches, grouped by package. |
1310
+ | `pageFrontmatter` | `function pageFrontmatter(page,` | {object} The frontmatter to write. | The frontmatter a page publishes with. |
1311
+ | `renderSitePage` | `renderSitePage(page, options)` | `{body, data, resolved, tableErrors, secretErrors, wikiErrors, imageErrors}` | Transform one page with the same passes the site writer uses. |
1312
+ | `renderPages` | `function renderPages(pages, options)` | `{{written: number, byKind: Record<string, number>, tableErrors: object[], wikiErrors: object[]}}` | Renders every page. |
1313
+ | `resolveSitePass` | `function resolveSitePass(name, options)` | `{{beforeLinks?: Function}}` The bundle. | Resolves `site.pass` to its bundle. |
1314
+ | `sitePageDecorator` | `sitePageDecorator(config, index)` | Function | Add the site's infobox and being metadata to a rendered page. |
1315
+ | `buildSite` | `buildSite({ config, sqlTables })` | `{{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], mapFindings: object[], manifests: object\|null, site?: object}}` | Collects, checks and renders a content tree's pages, and reports what it found; returns each page for the site's renderer. |
1317
1316
 
1318
1317
  `siteGates` takes `{ config, records, sqlTables? }`. Pass the prepared
1319
1318
  `sqlTables` map so anchors emitted by automatic dictionary sections are present in the
1320
1319
  index for checking page links.
1321
1320
 
1321
+ ### `engine.contentRender`
1322
+
1323
+ `createRenderer(target, options?)` builds the one markdown-it configuration every target renders through. `target` is `"foundry"`, `"web"`, `"preview"` or `"book"`. The renderer holds the configured parser (`md`), the names of the plugins installed (`plugins`), and three functions: `prepare` writes the constructs markdown-it has no syntax for — marked text, captioned items, named blocks, verse and alerts — as HTML between blank lines; `render` prepares and renders to HTML; `renderInline` renders one inline run. The `book` target parses only, and its Typst stage reads the tokens.
1324
+
1325
+ | Export | Signature | Returns | Use it when |
1326
+ | ---------------- | ---------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
1327
+ | `createRenderer` | `createRenderer(target, options?)` | `{ target, md, plugins, prepare, render, renderInline }` | Rendering a note body for one target with the rules every target shares. |
1328
+ | `RENDER_TARGETS` | Constant | The four targets | Checking that a target is one the renderer is built for. |
1329
+ | `highlightCode` | `highlightCode(code, language)` | highlight.js markup, or `""` for a language it does not know | Highlighting a fenced block's code at build time. |
1330
+ | `inertHtml` | `inertHtml(markdown)` | The markdown with each authored tag made inert | Preparing a buffer for the `preview` target, which writes the only HTML its parser sees. |
1331
+
1332
+ `createRenderer("preview", { source: true })` shows wikilinks, spans and expressions as highlighted authored source; without `source` the preview renders them. `options.icons` is the icon registry, or a function that returns it; `options.images` is `{ resolveSrc, lookupAsset }`, which resolves a body image's address and finds its asset.
1333
+
1334
+ ### `engine.previewIndex`
1335
+
1336
+ `previewIndex({ config, records, foreign? })` builds what [`renderPreview`](#enginesitepreview) takes as `index`: the link index, the asset address space, the languages' maps, the icon registry and an `expand` function that resolves a buffer's links, embeds, expressions and figure numbers as the website does. `records` are the content index's records and `foreign` the dependencies' indexes. Build it once per index and reuse it for every render.
1337
+
1338
+ | Export | Signature | Returns | Use it when |
1339
+ | -------------- | -------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------ |
1340
+ | `previewIndex` | `previewIndex({ config, records, foreign })` | `{ runs, icons, lookupAsset, expand }` | An editor holds a content index and wants the preview to render with it. |
1341
+
1322
1342
  ### `engine.sitePreview`
1323
1343
 
1324
- `renderPreview(text, { dark = false, infobox = true, file = "", currentDate = "720.1" })` renders the current Markdown buffer directly to HTML. Frontmatter is optional. It needs no package configuration, saved note, content tree, dependency cache, database or Pandoc installation. The result is `{ ok, html, frontmatter, findings }`; `file` only labels diagnostics. Malformed frontmatter produces a warning and remains visible without preventing a preview update.
1344
+ `renderPreview(text, { dark = false, infobox = true, file = "", currentDate = "720.1", index })` renders the current Markdown buffer directly to HTML. Frontmatter is optional. Without `index` it needs no package configuration, saved note, content tree, dependency cache, database or Pandoc installation. The result is `{ ok, html, frontmatter, findings }`; `file` only labels diagnostics. Malformed frontmatter produces a warning and remains visible without preventing a preview update.
1345
+
1346
+ With `index`, from [`engine.previewIndex`](#enginepreviewindex), the preview expands the buffer as the website expands a page and renders it through the same renderer: links resolve, embeds draw their pictures, marked text is mapped and set in its face, icons draw, figure numbers carry their asset roles, and a concealed link is concealed. A query, which a buffer cannot run, stays highlighted source.
1325
1347
 
1326
1348
  | Export | Signature | Returns | Use it when |
1327
1349
  | ------------------------ | ------------------------------- | ------------------------------------- | ---------------------------------------------------------------------- |
@@ -1340,7 +1362,7 @@ const result = renderPreview(currentBufferText, { dark: true });
1340
1362
  showDiagnostics(result.findings);
1341
1363
  ```
1342
1364
 
1343
- Ordinary Markdown, definition lists, footnotes, alert boxes, poetry, captions, local figures and container blocks render locally. Alerts reuse the four shared icons and titles, accept custom titles and nested alerts, and have opaque tinted backgrounds in both modes. Definition terms are bold; definitions retain ordinary Markdown styling. Wikilinks and image embeds retain their entire authored source: blue opening markers (including all of `![[`), brackets and pipes, purple addresses and anchors, bold foreground labels, and grey attributes. Spans retain their entire source in bold, with blue brackets and grey attributes. Code examples stay literal. Incomplete syntax remains visible while editing.
1365
+ Ordinary Markdown, definition lists, footnotes, alert boxes, verse, captions, local figures and container blocks render locally. Alerts reuse the four shared icons and titles, accept custom titles and nested alerts, and have opaque tinted backgrounds in both modes. Definition terms are bold; definitions retain ordinary Markdown styling. Wikilinks and image embeds retain their entire authored source: blue opening markers (including all of `![[`), brackets and pipes, purple addresses and anchors, bold foreground labels, and grey attributes. Spans retain their entire source in bold, with blue brackets and grey attributes. Code in a code span or an example fence stays literal, and a fence that names a language is highlighted. Incomplete syntax remains visible while editing.
1344
1366
 
1345
1367
  SQL and pagelist fences remain grey source, including delimiters. Calendar/date expressions, cross-note references and maps remain green source. A dependent call nested inside a pure helper preserves the entire outer expression. Pure helpers and scalar frontmatter expressions evaluate locally; same-note figure references and non-date event facts can resolve from this buffer. Infobox fields show authored values and green reference source; image and portrait wikilinks use the image syntax colors. Missing ages are calculated locally from neutral birth dates and `currentDate`, defaulting to `"720.1"`; authored ages, including `0`, take precedence. Unavailable ages remain `null` internally, have no infobox row, and render as empty text in `{{data.age}}`. Named-calendar birth dates remain unavailable without calendar context. Set `infobox: false` to omit the profile.
1346
1368
 
@@ -1499,24 +1521,6 @@ without a raw string it reads that file. The caller owns this map, and the
1499
1521
  link index leaves it unchanged. This source capture is separate from published
1500
1522
  metadata and has no persistent or cross-invocation cache.
1501
1523
 
1502
- ### `engine.webWikilinks`
1503
-
1504
- Wikilink resolution for the knowledgebase build. The same authored links the pack compilers turn into Foundry `@UUID` enrichers (see `./wikilinks.mjs`) become site-local hrefs here:
1505
-
1506
- | Export | Signature | Returns | Use it when |
1507
- | ---------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
1508
- | `frontmatterWikilinks` | `function frontmatterWikilinks(fm)` | {Array<{path: string, link: string}>} In reading order; `path` is the dotted key path of the offending value (`government.summary`, `aliases.1`). | Every wikilink authored inside a frontmatter value. |
1509
- | `resolveWebWikilinks` | `function resolveWebWikilinks(body, ctx)` | {string} The body with wikilinks rewritten. | Rewrites the wikilinks in a markdown body as KB-local markdown links. |
1510
-
1511
- Both `resolveWebWikilinks` and Foundry's `convertWikilinks` derive reference
1512
- spoilers from the resolved note audience and secret-anchor metadata. A trailing
1513
- `{.show}` bypasses automatic spoiler wrapping, without changing destination
1514
- access. A web destination excluded by audience retains its label without an
1515
- href. Same-note secret anchors follow the same rule; a whole-note link to a
1516
- public note does not inherit individual secret sections. See the
1517
- [authoring contract](authoring/links-and-markup.md#references-to-secret-subjects)
1518
- for surface presentation.
1519
-
1520
1524
  ### `engine.contentTables`
1521
1525
 
1522
1526
  Prepared SQL content tables are expanded into Markdown before link resolution.
@@ -1627,20 +1631,36 @@ source locations and frontmatter source slicing.
1627
1631
 
1628
1632
  ### `engine.wikilinks`
1629
1633
 
1630
- Wikilink resolution for the pack compilers. Content notes link to one another with wikilinks rather than file paths:
1631
-
1632
- | Export | Signature | Returns | Use it when |
1633
- | -------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1634
- | `resolveItemDocType` | `function resolveItemDocType(qualifier, types)` | {string\|null} The underlying document type, or `null` when the qualifier is not a virtual one. | Reads an accepted `doc<type>` input alias as its authored type. |
1635
- | `readQualifier` | `function readQualifier(target, types, packages, noIndexPackages)` | {{type: string, shortcode: string, itemDoc: boolean, package?: string, system?: string, reason?: undefined} \| {reason: "unknown-type"\|"no-content-index", package?: string} \| null} The resolved qualifier; a `reason` when the target is definitely qualified but names no known type or no fetched index; or `null` when it is not an address at all. | Read a link target as a **qualified** `type-shortcode` reference, or report that it does not parse as one. |
1636
- | `anchorPageId` | `function anchorPageId(noteId, anchorSlug)` | {string} A 16-character alphanumeric id. | The deterministic JournalEntryPage id for one anchor: SHA-256 of `"<noteId>-<anchorSlug>"`, base64-encoded, reduced to the 16 alphanumeric characters a Foundry id allows. |
1637
- | `buildWikilinkIndex` | `function buildWikilinkIndex(docs, packageId, foreign, contentPackage)` | `{{byShortcode: Map<string, object>, types: Set<string>}}` `types` is every type the tree actually contains, so a qualifier naming no real type can be told apart from a missing target. | Builds the link-resolution tables for a content tree. |
1638
- | `resolveReference` | `function resolveReference(index, ref, hint)` | {{name?: string, uuid?: string, address?: string, subType?: string}\|undefined} The target, or `undefined` where nothing answers. | Resolve one reference — a bare shortcode, a short address or a canonical one — to what a compendium can use. |
1639
- | `convertWikilinks` | `function convertWikilinks(markdown,` | `{{markdown: string, unresolved: Array<{link: string, target: string, offset: number, reason: string, packages?: string[], anchor?: string}>}}` Each `reason` is one of {@link LINK_FINDING_REASONS}, the vocabulary all three resolvers share — `ambiguous` carries the claiming `packages` and `unknown-anchor` the section it named. | Rewrites every wikilink in a markdown body as a Foundry UUID enricher. |
1634
+ The link resolver. One authored `[[…]]` resolves the same way for every target, and each target writes the result in its own form: a Foundry journal gets an `@UUID[…]{label}` content link that survives import into a world, a web page a relative URL, and the book a cross-reference to the entry it prints. `resolveWikilinks` is the resolution; a `LinkView` is the index it reads (`packLinkView` over the pack compile's index, `siteLinkView` over the site index), and `LINK_WRITERS` holds each target's form. `convertWikilinks` and `resolveWebWikilinks` are the pack compile's and the site's entries to it.
1635
+
1636
+ | Export | Signature | Returns | Use it when |
1637
+ | ---------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1638
+ | `resolveItemDocType` | `function resolveItemDocType(qualifier, types)` | {string\|null} The underlying document type, or `null` when the qualifier is not a virtual one. | Reads an accepted `doc<type>` input alias as its authored type. |
1639
+ | `readQualifier` | `function readQualifier(target, types, packages, noIndexPackages)` | {{type: string, shortcode: string, itemDoc: boolean, package?: string, system?: string, reason?: undefined} \| {reason: "unknown-type"\|"no-content-index", package?: string} \| null} The resolved qualifier; a `reason` when the target is definitely qualified but names no known type or no fetched index; or `null` when it is not an address at all. | Read a link target as a **qualified** `type-shortcode` reference, or report that it does not parse as one. |
1640
+ | `slugify` | `slugify(text)` | `string` | The heading and anchor slug: the rule that names a page also names a section within it. |
1641
+ | `anchorPageId` | `function anchorPageId(noteId, anchorSlug)` | {string} A 16-character alphanumeric id. | The deterministic JournalEntryPage id for one anchor: SHA-256 of `"<noteId>-<anchorSlug>"`, base64-encoded, reduced to the 16 alphanumeric characters a Foundry id allows. |
1642
+ | `buildWikilinkIndex` | `function buildWikilinkIndex(docs, packageId, foreign, contentPackage)` | `{{byShortcode: Map<string, object>, types: Set<string>}}` `types` is every type the tree actually contains, so a qualifier naming no real type can be told apart from a missing target. | Builds the link-resolution tables for a content tree. |
1643
+ | `LINK_WRITERS` | `const LINK_WRITERS` | `{foundry, web, book}`, each `(link: ResolvedLink) => string` | The form each target writes a resolved link in, a draft link, a link with nothing to open and an unresolved one. |
1644
+ | `resolveWikilinks` | `function resolveWikilinks(markdown, { view, write, captionLabels })` | `{{markdown: string, links: ResolvedLink[], findings: object[], resolved: object[]}}` The written body; every link's neutral resolution, in body order; each finding with its `reason`, `offset` and `occurrence`; and every destination outside the page. | Resolve every wikilink in a body against one index and write each in one target's form. |
1645
+ | `packLinkView` | `function packLinkView(index, { type, id, pack, docPack })` | `LinkView` | The pack compile's index, answered with compendium UUIDs, for links written on one note. |
1646
+ | `convertWikilinks` | `function convertWikilinks(markdown,` | `{{markdown: string, links: ResolvedLink[], unresolved: Array<{link: string, target: string, offset: number, reason: string, packages?: string[], anchor?: string}>}}` Each `reason` is one of {@link LINK_FINDING_REASONS} — `ambiguous` carries the claiming `packages` and `unknown-anchor` the section it named. | Rewrites every wikilink in a markdown body as a Foundry UUID enricher. |
1647
+ | `resolveReference` | `function resolveReference(index, ref, hint)` | {{name?: string, uuid?: string, address?: string, subType?: string}\|undefined} The target, or `undefined` where nothing answers. | Resolve one reference — a bare shortcode, a short address or a canonical one — to what a compendium can use. |
1648
+ | `siteLinkView` | `function siteLinkView(ctx, body)` | `LinkView` | The site index, answered with URLs, for links written on one page. |
1649
+ | `frontmatterWikilinks` | `function frontmatterWikilinks(fm)` | {Array<{path: string, link: string}>} In reading order; `path` is the dotted key path of the offending value (`government.summary`, `aliases.1`). | Every wikilink authored inside a frontmatter value. |
1650
+ | `resolveWebWikilinks` | `function resolveWebWikilinks(body, ctx)` | {string} The body with embeds and wikilinks rewritten. | Rewrites the embeds and wikilinks in a markdown body for the website, or for the book with `ctx.output` `"book"`. |
1651
+
1652
+ Every target derives reference
1653
+ spoilers from the resolved note audience and secret-anchor metadata. A trailing
1654
+ `{.show}` bypasses automatic spoiler wrapping, without changing destination
1655
+ access. A web destination excluded by audience retains its label without an
1656
+ href. Same-note secret anchors follow the same rule; a whole-note link to a
1657
+ public note does not inherit individual secret sections. See the
1658
+ [authoring contract](authoring/links-and-markup.md#references-to-secret-subjects)
1659
+ for surface presentation.
1640
1660
 
1641
1661
  ### `engine.wikilinkSyntax`
1642
1662
 
1643
- What a `[[…]]` **is**, before anything decides where it points. One authored link compiles to two different addresses — a Foundry `@UUID` enricher for the packs, a URL for the web — and those two destinations are the _only_ thing that legitimately differs. The syntax is the author's, and it is the same syntax whichever build is reading it.
1663
+ What a `[[…]]` **is**, before anything decides where it points. One authored link is written three ways — a Foundry `@UUID` content link for the packs, a URL for the web, a cross-reference for the book — and those forms are the _only_ thing that legitimately differs. The syntax is the author's, and it is the same syntax whichever build is reading it.
1644
1664
 
1645
1665
  | Export | Signature | Returns | Use it when |
1646
1666
  | -------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
@@ -1649,7 +1669,7 @@ What a `[[…]]` **is**, before anything decides where it points. One authored l
1649
1669
  | `readWikilink` | `function readWikilink(inner, context, literal?)` | Typed target or literal target with diagnostic source. | Parse a link target using complete builder Address context. |
1650
1670
  | `authoredLabel` | `authoredLabel({ display })` | {string\|null} The label, or `null` when there is none to show. | The label an author actually supplied, or `null` when they supplied none. |
1651
1671
  | `unlabelledLinkMessage` | `function unlabelledLinkMessage(target)` | {string} The message, unpunctuated at the end as a finding is. | What an author writing an unlabelled link is told, in one place. |
1652
- | `LINK_FINDING_REASONS` | `const LINK_FINDING_REASONS` | — | Every way a link can fail, named once for all three resolvers. |
1672
+ | `LINK_FINDING_REASONS` | `const LINK_FINDING_REASONS` | — | Every way a link can fail, named once for every reader of a link. |
1653
1673
  | `unresolvedAddressMessage` | `function unresolvedAddressMessage(target)` | {string} The message, unpunctuated at the end as a finding is. | What an author writing an address that resolves to nothing is told. |
1654
1674
  | `stubAddressMessage` | `function stubAddressMessage(target, file)` | {string} The message, unpunctuated at the end as a finding is. | What an author linking to a stub is told, naming the file to open. |
1655
1675
  | `ambiguousAddressMessage` | `function ambiguousAddressMessage(target, packages` | {string} The message. | What an author writing a short address more than one package publishes is told. |
@@ -2087,7 +2107,7 @@ const config = defineConfig({
2087
2107
  | `DEFAULT_ADDRESS_SCHEME` | `const DEFAULT_ADDRESS_SCHEME` | — | reading an unconfigured repository's address-scheme defaults (`prefix`, where the content tree mounts inside the package) |
2088
2108
  | `DERIVED_SYSTEM_VERSION` | `const DERIVED_SYSTEM_VERSION` | — | the loader-only symbol key `defineConfig` uses internally to receive a resolved system version; not something a configuration author writes |
2089
2109
  | `publishesContentPages` | `publishesContentPages(config)` | `boolean` | checking whether the authored content tree contains notes beyond its homepage, shared by site, PDF, index, and Foundry address emission |
2090
- | `DERIVED_HUGO_KEYS` | `const DERIVED_HUGO_KEYS` | — | reading which Hugo keys `site.hugo` may not declare because the site build generates them, each naming its source (declaring one is an error naming the key); a dotted key covers everything beneath it |
2110
+ | `siteBase` | `siteBase(config)` | `string` | reading the path a package's site is served at — `site.base` where the configuration states one, `/<contentPackage>/` otherwise |
2091
2111
 
2092
2112
  ## `./config`
2093
2113
 
@@ -2101,13 +2121,13 @@ const config = loadPackageBuildConfig();
2101
2121
  console.log(config.stageDir);
2102
2122
  ```
2103
2123
 
2104
- | Export | Signature | Returns | Use it when |
2105
- | --------------------------- | ----------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2106
- | `DERIVED_MANIFEST_KEYS` | `const DERIVED_MANIFEST_KEYS` | — | reading which manifest keys a repository may not declare because the build derives them (declaring one is an error naming the key) |
2107
- | `resolvePackageBuildConfig` | `resolvePackageBuildConfig(shared)` | `Readonly<PackageBuildConfig>` | validating the `packageBuild:` section from an already-loaded shared configuration — the pure half, usable without touching disk |
2108
- | `loadPackageBuildConfig` | `loadPackageBuildConfig()` | `Readonly<PackageBuildConfig>` | reading and validating the repository's resolved package-build configuration from disk, read fresh on each call rather than cached at import |
2109
- | `checkHomepage` | `checkHomepage(homepage, contentPackage)` | `void` | validating a resolved `homepage` against `contentPackage` — called by `package-build site` before the generated `baseURL` is written, not by `resolvePackageBuildConfig` itself |
2110
- | `fail` | `fail(where, problem)` | `never` | rejecting a configured value from a sibling module, naming the dotted key path it was written under, so the loader can resolve it to a line and column |
2124
+ | Export | Signature | Returns | Use it when |
2125
+ | --------------------------- | ----------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2126
+ | `DERIVED_MANIFEST_KEYS` | `const DERIVED_MANIFEST_KEYS` | — | reading which manifest keys a repository may not declare because the build derives them (declaring one is an error naming the key) |
2127
+ | `resolvePackageBuildConfig` | `resolvePackageBuildConfig(shared)` | `Readonly<PackageBuildConfig>` | validating the `packageBuild:` section from an already-loaded shared configuration — the pure half, usable without touching disk |
2128
+ | `loadPackageBuildConfig` | `loadPackageBuildConfig()` | `Readonly<PackageBuildConfig>` | reading and validating the repository's resolved package-build configuration from disk, read fresh on each call rather than cached at import |
2129
+ | `checkHomepage` | `checkHomepage(homepage, contentPackage)` | `void` | validating a resolved `homepage` against `contentPackage` — called by `package-build site build` before the site settings are composed, not by `resolvePackageBuildConfig` itself |
2130
+ | `fail` | `fail(where, problem)` | `never` | rejecting a configured value from a sibling module, naming the dotted key path it was written under, so the loader can resolve it to a line and column |
2111
2131
 
2112
2132
  ## `./prettier`
2113
2133
 
@@ -2439,12 +2459,12 @@ Not a JavaScript module — this subpath entry exists so tooling (bundlers, `imp
2439
2459
 
2440
2460
  The engine wildcard subpath also exposes the body parsers below. Their figure exports operate on leading captions.
2441
2461
 
2442
- | Module | Exports | Contract |
2443
- | ---------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2444
- | `engine/content-figures.mjs` | `FIGURE_NAMES`, `FIGURE_CLASSES`, `scanFigures`, `numberFigures`, `renderFigureBlocks` | Scan leading captions and their next supported blocks; `numbered` distinguishes `:` and `:@`. `type` may override inference; `example` is supported. `FIGURE_CLASSES` retains built-in styled classes, not a closed author vocabulary. |
2445
- | `engine/content-blocks.mjs` | `BLOCK_NAMES`, `BLOCK_CONTAINERS`, `scanBlocks`, `renderBlocks`, `renderWithheldSections` | Read div and secret fences; generic div records have `name: "div"` and no generated title. Scanning includes poetry errors. |
2446
- | `engine/content-poetry.mjs` | `verseLines`, `poetryStanzas`, `inlineVerse`, `poetryMarkdown`, `scanPoetry`, `renderPoetry` | Read `poetry` code fences, validate authored metadata and tabs, and preserve relative indent levels and stanzas. |
2447
- | `engine/content-spans.mjs` | `spanMarkdownPlugin`, `scanSpans`, `renderSpans` | Parse bracket spans after recognized links. The scanner returns spans, the exact `.name` inventory and located errors, retaining visible text and attribute positions; rendering preserves inline Markdown and permitted attributes. |
2462
+ | Module | Exports | Contract |
2463
+ | ---------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2464
+ | `engine/content-figures.mjs` | `FIGURE_NAMES`, `FIGURE_CLASSES`, `scanFigures`, `numberFigures`, `renderFigureBlocks` | Scan leading captions and their next supported blocks; `numbered` distinguishes `:` and `:@`. `type` may override inference; `example` is supported. `FIGURE_CLASSES` retains built-in styled classes, not a closed author vocabulary. |
2465
+ | `engine/content-blocks.mjs` | `BLOCK_NAMES`, `BLOCK_CONTAINERS`, `scanBlocks`, `renderBlocks`, `renderWithheldSections` | Read div and secret fences; generic div records have `name: "div"` and no generated title. Scanning includes verse errors. |
2466
+ | `engine/content-verse.mjs` | `verseLines`, `verseStanzas`, `inlineVerse`, `verseMarkdown`, `scanVerse`, `renderVerse` | Read `verse` code fences, validate authored metadata and tabs, and preserve relative indent levels and stanzas. |
2467
+ | `engine/content-spans.mjs` | `spanMarkdownPlugin`, `scanSpans`, `renderSpans` | Parse bracket spans after recognized links. The scanner returns spans, the exact `.name` inventory and located errors, retaining visible text and attribute positions; rendering preserves inline Markdown and permitted attributes. |
2448
2468
 
2449
2469
  Inline `.secret` spans preserve their inline content, name classes and
2450
2470
  attributes. Site rendering uses a separate show/hide button and content span,
@@ -2535,16 +2555,17 @@ The wildcard engine subpath exposes
2535
2555
  `@heroiclands/package-build/engine/dictionary-rows` for deriving the
2536
2556
  `dictionary` and `terms` SQL relations from content-index records:
2537
2557
 
2538
- | Export | Signature | Returns | Use it when |
2539
- | ----------------------- | ----------------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------- |
2540
- | `DICTIONARY_COLUMNS` | `const DICTIONARY_COLUMNS` | A readonly list of column names | Declaring the schema of a language's dictionary. |
2541
- | `TERM_COLUMNS` | `const TERM_COLUMNS` | A readonly list of column names | Declaring the schema of owner-specific glossary terms. |
2542
- | `ETYMOLOGY_KEYS` | `const ETYMOLOGY_KEYS` | A readonly list of keys | Reading the structure of a dictionary word's etymology. |
2543
- | `dictionaryLetter` | `dictionaryLetter(text)` | `string` | Deriving a heading letter from a sort key. |
2544
- | `dictionaryEntryAnchor` | `dictionaryEntryAnchor(language, name)` | `string` | Deriving the stable entry anchor for a word in a language's dictionary. |
2545
- | `dictionaryAndTermRows` | `dictionaryAndTermRows(records, all = records)` | `{ dictionary: object[], terms: object[] }` | Deriving dictionary words and glossary terms for the selected records. |
2546
- | `serializeRows` | `serializeRows(rows)` | `string` | Writing deterministic JSON Lines with keys sorted at every depth. |
2547
- | `pluralOf` | `pluralOf(word, grammar)` | `string \| null` | Deriving a noun's plural: its own `plural`, else the first `grammar.plural` row whose ending fits. |
2558
+ | Export | Signature | Returns | Use it when |
2559
+ | ----------------------- | ----------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
2560
+ | `DICTIONARY_COLUMNS` | `const DICTIONARY_COLUMNS` | A readonly list of column names | Declaring the schema of a language's dictionary. |
2561
+ | `TERM_COLUMNS` | `const TERM_COLUMNS` | A readonly list of column names | Declaring the schema of owner-specific glossary terms. |
2562
+ | `ETYMOLOGY_KEYS` | `const ETYMOLOGY_KEYS` | A readonly list of keys | Reading the structure of a dictionary word's etymology. |
2563
+ | `dictionaryLetter` | `dictionaryLetter(text)` | `string` | Deriving a heading letter from a sort key. |
2564
+ | `dictionaryEntryAnchor` | `dictionaryEntryAnchor(language, name)` | `string` | Deriving the stable entry anchor for a word in a language's dictionary. |
2565
+ | `dictionaryAndTermRows` | `dictionaryAndTermRows(records, all = records)` | `{ dictionary: object[], terms: object[] }` | Deriving dictionary words and glossary terms for the selected records. |
2566
+ | `serializeRows` | `serializeRows(rows)` | `string` | Writing deterministic JSON Lines with keys sorted at every depth. |
2567
+ | `pluralOf` | `pluralOf(word, grammar)` | `string \| null` | Deriving a noun's plural: its own `plural`, else the first `grammar.plural` row whose ending fits. |
2568
+ | `pluralByRule` | `pluralByRule(form, grammar)` | `string \| null` | Deriving the plural the `grammar.plural` rule gives any form, whatever its class: the first row whose ending fits. |
2548
2569
 
2549
2570
  `dictionaryEntryAnchor` takes the language's canonical Address and the
2550
2571
  word's form. Its `word-` anchor is derived from the full SHA-256 digest of