@heroiclands/package-build 23.1.0 → 23.2.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 (248) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/bin/content-commands.mjs +68 -149
  3. package/bin/package-build.mjs +56 -10
  4. package/ci/check-banners.mjs +42 -6
  5. package/ci/confirm-npm-version.mjs +135 -0
  6. package/ci/content-format-reference.mjs +14 -5
  7. package/config.mjs +1 -0
  8. package/container.mjs +185 -4
  9. package/content-config.mjs +333 -210
  10. package/deploy.mjs +116 -10
  11. package/docs/api.md +238 -246
  12. package/docs/authoring/assets.md +11 -5
  13. package/docs/authoring/frontmatter.md +1 -1
  14. package/docs/authoring/links-and-markup.md +87 -0
  15. package/docs/commands.md +138 -99
  16. package/docs/configuration.md +277 -125
  17. package/docs/content-format.md +2 -0
  18. package/docs/diagnostics.md +48 -13
  19. package/docs/guides/book.md +341 -21
  20. package/docs/guides/e2e.md +13 -2
  21. package/docs/guides/icons.md +14 -4
  22. package/docs/guides/module-project.md +18 -8
  23. package/docs/guides/release-and-deploy.md +54 -6
  24. package/docs/guides/site.md +5 -2
  25. package/docs/project-setup.md +30 -10
  26. package/docs/reference/format-details.md +373 -170
  27. package/docs/reference/note-types.md +128 -109
  28. package/docs/reference/theme-pages.md +31 -6
  29. package/e2e.mjs +140 -28
  30. package/engine/actor-compiler.mjs +22 -31
  31. package/engine/address-charset.mjs +4 -17
  32. package/engine/address-fields.mjs +1 -1
  33. package/engine/address-render.mjs +12 -1
  34. package/engine/address-values.mjs +6 -0
  35. package/engine/address.mjs +46 -33
  36. package/engine/art-fields.mjs +30 -5
  37. package/engine/art-slots.mjs +10 -1
  38. package/engine/asset-bindings.mjs +12 -1
  39. package/engine/base-compiler.mjs +34 -65
  40. package/engine/being-age.mjs +2 -7
  41. package/engine/being-measurements.mjs +12 -1
  42. package/engine/calendar-artifacts.mjs +6 -0
  43. package/engine/calendar-choice.mjs +12 -1
  44. package/engine/calendar-format.mjs +12 -1
  45. package/engine/calendar-human.mjs +12 -1
  46. package/engine/compile-corpus.mjs +3 -1
  47. package/engine/content-address.mjs +9 -12
  48. package/engine/content-alerts.mjs +13 -1
  49. package/engine/content-blocks.mjs +14 -3
  50. package/engine/content-embeds.mjs +11 -3
  51. package/engine/content-figures.mjs +9 -0
  52. package/engine/content-footnotes.mjs +12 -1
  53. package/engine/content-format-check.mjs +11 -262
  54. package/engine/content-format.yaml +5 -2
  55. package/engine/content-images.mjs +3 -0
  56. package/engine/content-index.mjs +443 -20
  57. package/engine/content-links.mjs +29 -35
  58. package/engine/content-lint.mjs +2 -3
  59. package/engine/content-poetry.mjs +12 -1
  60. package/engine/content-slug.mjs +2 -14
  61. package/engine/content-spans.mjs +13 -1
  62. package/engine/content-tables.mjs +12 -1
  63. package/engine/culture-choice.mjs +210 -13
  64. package/engine/data-keys.mjs +19 -16
  65. package/engine/date-conversion.mjs +12 -1
  66. package/engine/declaration-check.mjs +9 -0
  67. package/engine/derived-fields.mjs +6 -7
  68. package/engine/diagnostics.mjs +34 -4
  69. package/engine/docs-checks.mjs +9 -0
  70. package/engine/document-subtypes.mjs +47 -34
  71. package/engine/event-views.mjs +139 -14
  72. package/engine/exported-scene.mjs +92 -28
  73. package/engine/extension-attributes.mjs +12 -1
  74. package/engine/field-spec.mjs +11 -102
  75. package/engine/folder-notes.mjs +0 -64
  76. package/engine/foreign-catalog.mjs +2 -1
  77. package/engine/format-generated.mjs +9 -0
  78. package/engine/foundry-entries.mjs +10 -28
  79. package/engine/frontmatter-lint.mjs +83 -392
  80. package/engine/frontmatter.mjs +20 -70
  81. package/engine/generate.mjs +10 -15
  82. package/engine/heading-attributes.mjs +12 -1
  83. package/engine/helpers.mjs +41 -102
  84. package/engine/holdings.mjs +1 -1
  85. package/engine/homepage.mjs +3 -10
  86. package/engine/ids.mjs +1 -167
  87. package/engine/index-records.mjs +6 -4
  88. package/engine/index.mjs +0 -3
  89. package/engine/infobox-render.mjs +15 -3
  90. package/engine/infobox.mjs +13 -14
  91. package/engine/item-compiler.mjs +4 -10
  92. package/engine/item-docs.mjs +3 -5
  93. package/engine/item-registry.mjs +2 -10
  94. package/engine/journals.mjs +1 -5
  95. package/engine/literature-notes.mjs +4 -12
  96. package/engine/macros.mjs +1 -2
  97. package/engine/map-graphviz.mjs +12 -1
  98. package/engine/markdown-expressions.mjs +12 -1
  99. package/engine/markdownlint-run.mjs +567 -0
  100. package/engine/metadata-index.mjs +42 -12
  101. package/engine/note-addresses.mjs +143 -35
  102. package/engine/note-claims.mjs +14 -28
  103. package/engine/note-dates.mjs +3 -3
  104. package/engine/note-frontmatter.mjs +6 -0
  105. package/engine/note-ids.mjs +8 -14
  106. package/engine/note-package.mjs +8 -25
  107. package/engine/note-state.mjs +1 -3
  108. package/engine/note-vocabulary.mjs +148 -84
  109. package/engine/number-words.mjs +12 -1
  110. package/engine/pack-config.mjs +15 -25
  111. package/engine/pack-router.mjs +10 -16
  112. package/engine/pathnames.mjs +8 -22
  113. package/engine/pdf-build.mjs +773 -121
  114. package/engine/pdf-coverage.mjs +188 -0
  115. package/engine/pdf-index.mjs +54 -7
  116. package/engine/pdf-pages.mjs +247 -0
  117. package/engine/pdf-render.mjs +114 -4
  118. package/engine/pdf-toc.mjs +29 -32
  119. package/engine/project-init.mjs +46 -6
  120. package/engine/prose-config.mjs +1 -1
  121. package/engine/prose-lint.mjs +52 -88
  122. package/engine/readability-lint.mjs +9 -0
  123. package/engine/reckoning-markers.mjs +12 -1
  124. package/engine/reference-targets.mjs +7 -0
  125. package/engine/runtime-only-fields.mjs +5 -8
  126. package/engine/scene-levels.mjs +2 -2
  127. package/engine/scenes.mjs +1 -1
  128. package/engine/shortcode-references.mjs +7 -0
  129. package/engine/site-build.mjs +30 -16
  130. package/engine/site-index.mjs +10 -16
  131. package/engine/site-preview.mjs +6 -0
  132. package/engine/site-root.mjs +8 -1
  133. package/engine/social-tie-terms.mjs +12 -1
  134. package/engine/social-ties.mjs +3 -0
  135. package/engine/sql-tables.mjs +56 -20
  136. package/engine/standing-terms.mjs +11 -1
  137. package/engine/standings.mjs +11 -27
  138. package/engine/system-block.mjs +39 -141
  139. package/engine/system-vocabulary.mjs +6 -0
  140. package/engine/walk-files.mjs +69 -0
  141. package/engine/web-wikilinks.mjs +23 -3
  142. package/engine/wikilink-syntax.mjs +32 -21
  143. package/engine/wikilinks.mjs +27 -16
  144. package/engine/yaml-lint.mjs +7 -4
  145. package/githooks/attribution.mjs +8 -0
  146. package/githooks/commit-message.mjs +9 -0
  147. package/hm3/actors.mjs +9 -48
  148. package/hm3/default-item-art.mjs +9 -9
  149. package/hm3/item-fields.mjs +2 -9
  150. package/hm3/template-priority.mjs +6 -7
  151. package/hugo-theme/data/banner-kinds.yaml +78 -0
  152. package/hugo-theme/data/banners.yaml +2 -3
  153. package/hugo-theme/layouts/404.html +3 -3
  154. package/hugo-theme/layouts/partials/banner-url.html +31 -7
  155. package/hugo-theme/layouts/partials/hero-banner.html +13 -14
  156. package/hugo-theme/layouts/partials/related.html +1 -1
  157. package/hugo-theme/layouts/partials/warn-missing-banner.html +1 -1
  158. package/hugo-theme/static/css/style.css +1 -1
  159. package/manifest.mjs +11 -4
  160. package/markdownlint-config.mjs +1 -1
  161. package/package.json +6 -3
  162. package/release.mjs +83 -18
  163. package/sohl/actors.mjs +16 -27
  164. package/sohl/default-item-art.mjs +3 -3
  165. package/sohl/document-subtypes.mjs +1 -16
  166. package/sohl/infobox.mjs +2 -3
  167. package/sohl/item-builders.mjs +5 -8
  168. package/sohl/item-fields.mjs +5 -35
  169. package/sohl/items.mjs +0 -4
  170. package/sohl/kb-passes.mjs +3 -3
  171. package/types/container.d.mts +116 -5
  172. package/types/content-config.d.mts +83 -35
  173. package/types/deploy.d.mts +42 -6
  174. package/types/e2e.d.mts +35 -3
  175. package/types/engine/address-charset.d.mts +4 -17
  176. package/types/engine/address.d.mts +17 -8
  177. package/types/engine/art-fields.d.mts +7 -4
  178. package/types/engine/art-slots.d.mts +6 -0
  179. package/types/engine/base-compiler.d.mts +15 -17
  180. package/types/engine/content-address.d.mts +9 -12
  181. package/types/engine/content-embeds.d.mts +4 -2
  182. package/types/engine/content-format-check.d.mts +0 -64
  183. package/types/engine/content-index.d.mts +82 -3
  184. package/types/engine/content-links.d.mts +2 -4
  185. package/types/engine/culture-choice.d.mts +51 -8
  186. package/types/engine/data-keys.d.mts +2 -2
  187. package/types/engine/derived-fields.d.mts +1 -2
  188. package/types/engine/diagnostics.d.mts +27 -4
  189. package/types/engine/document-subtypes.d.mts +12 -31
  190. package/types/engine/event-views.d.mts +14 -2
  191. package/types/engine/exported-scene.d.mts +3 -2
  192. package/types/engine/field-spec.d.mts +13 -122
  193. package/types/engine/folder-notes.d.mts +0 -39
  194. package/types/engine/foreign-catalog.d.mts +2 -1
  195. package/types/engine/foundry-entries.d.mts +4 -5
  196. package/types/engine/frontmatter-lint.d.mts +2 -2
  197. package/types/engine/frontmatter.d.mts +19 -45
  198. package/types/engine/generate.d.mts +1 -1
  199. package/types/engine/helpers.d.mts +25 -42
  200. package/types/engine/homepage.d.mts +3 -10
  201. package/types/engine/ids.d.mts +0 -114
  202. package/types/engine/index-records.d.mts +6 -4
  203. package/types/engine/index.d.mts +0 -1
  204. package/types/engine/infobox-render.d.mts +7 -1
  205. package/types/engine/infobox.d.mts +3 -2
  206. package/types/engine/item-docs.d.mts +1 -1
  207. package/types/engine/item-registry.d.mts +2 -4
  208. package/types/engine/literature-notes.d.mts +1 -1
  209. package/types/engine/markdownlint-run.d.mts +40 -0
  210. package/types/engine/metadata-index.d.mts +9 -1
  211. package/types/engine/note-addresses.d.mts +27 -2
  212. package/types/engine/note-claims.d.mts +3 -5
  213. package/types/engine/note-ids.d.mts +2 -4
  214. package/types/engine/note-package.d.mts +1 -1
  215. package/types/engine/note-state.d.mts +1 -3
  216. package/types/engine/note-vocabulary.d.mts +28 -21
  217. package/types/engine/pack-config.d.mts +2 -6
  218. package/types/engine/pack-router.d.mts +4 -4
  219. package/types/engine/pathnames.d.mts +3 -3
  220. package/types/engine/pdf-build.d.mts +98 -12
  221. package/types/engine/pdf-coverage.d.mts +60 -0
  222. package/types/engine/pdf-index.d.mts +34 -5
  223. package/types/engine/pdf-pages.d.mts +143 -0
  224. package/types/engine/pdf-render.d.mts +45 -1
  225. package/types/engine/pdf-toc.d.mts +12 -14
  226. package/types/engine/prose-config.d.mts +1 -1
  227. package/types/engine/prose-lint.d.mts +11 -10
  228. package/types/engine/runtime-only-fields.d.mts +2 -2
  229. package/types/engine/site-build.d.mts +9 -5
  230. package/types/engine/site-index.d.mts +4 -5
  231. package/types/engine/site-root.d.mts +1 -1
  232. package/types/engine/sql-tables.d.mts +31 -2
  233. package/types/engine/standing-terms.d.mts +2 -1
  234. package/types/engine/standings.d.mts +7 -9
  235. package/types/engine/system-block.d.mts +10 -44
  236. package/types/engine/walk-files.d.mts +15 -0
  237. package/types/engine/wikilink-syntax.d.mts +18 -19
  238. package/types/engine/yaml-lint.d.mts +5 -4
  239. package/types/hm3/actors.d.mts +1 -2
  240. package/types/hm3/item-fields.d.mts +2 -4
  241. package/types/hm3/template-priority.d.mts +6 -7
  242. package/types/manifest.d.mts +3 -2
  243. package/types/release.d.mts +12 -7
  244. package/types/sohl/actors.d.mts +2 -1
  245. package/types/sohl/document-subtypes.d.mts +1 -2
  246. package/types/sohl/item-fields.d.mts +3 -7
  247. package/engine/retired-fields.mjs +0 -631
  248. package/types/engine/retired-fields.d.mts +0 -354
@@ -43,15 +43,21 @@ role: portrait
43
43
  | A note writes | The page draws |
44
44
  | ------------------ | -------------------------------------------------- |
45
45
  | an asset Address | that picture |
46
- | a blank | no picture, on purpose, and no default replaces it |
46
+ | `none` | no picture, on purpose, and no default replaces it |
47
47
  | nothing, or `null` | a default picture chosen by the note's `type` |
48
48
 
49
49
  Omitting the key does not mean "no hero image" — it means artwork chosen for
50
50
  the author, which inverts the ordinary reading of an absent key. **A note
51
- declines with a blank, not with `none`.** `none` parses as an ordinary asset
52
- Address — a shortcode, not a keyword — and since nothing in the corpus
53
- answers to it, the build drops the key outright and the page falls through to
54
- its type's default: the opposite of a decline.
51
+ declines with `none`, and with nothing else.** `none` is a keyword, never an
52
+ asset shortcode, so it is not looked up. A blank (`""`) is an error, and
53
+ `null` is the same as omitting the key. An Address that names no image in
54
+ this package or a declared dependency is a finding at its line and column;
55
+ write `data.banner: none` when the page is meant to carry no hero image.
56
+
57
+ ```yaml
58
+ data:
59
+ banner: none
60
+ ```
55
61
 
56
62
  `role` is one of `portrait`, `emblem`, `banner`, `plate` or `map`, and belongs to the `image` type only — declaring it in a `provenance.yaml` under `assets/icons` is a finding, since an icon carries one nominal size per medium whatever the file holds. Leave the key out for an ordinary picture; there is no default role name, only its absence.
57
63
 
@@ -31,7 +31,7 @@ The formatter keeps a collection on one line when the full line is under 100 cha
31
31
 
32
32
  ## Shared facts and game mechanics
33
33
 
34
- The [note-type reference](../reference/note-types.md) lists every field accepted under `data`, including the shared `id`, `pack`, `packFolder`, `harnworld`, `icon`, and `banner` fields. A type such as `being` also has its own fields, including `culture`, `homes`, and `affiliations`. A field absent from a type's vocabulary is a lint error; an unrelated field is not silently repurposed.
34
+ The [note-type reference](../reference/note-types.md) lists every field accepted under `data`, including the shared `id`, `pack`, `packFolder`, `harnworld`, and `banner` fields. A type such as `being` also has its own fields, including `homes` and `affiliations`. `culture` is shared by `being`, `place`, `affiliation`, `lore` and `doc`: it names a `lore` note of `subType: culture`, is required on a `doc` of `subType: settingguide`, and is inherited down `data.parents` by places and affiliations. See [culture in the index](../reference/format-details.md#culture-in-the-index). A field absent from a type's vocabulary is a lint error; an unrelated field is not silently repurposed.
35
35
 
36
36
  A being's `data.archetypes` names the roles it can fill in an adventure. Choose every specific role that fits, such as `entertainer` for a performer or `guildsperson` for someone whose professional training or connections matter. Use `[commoner]` when no specific archetype fits. `commoner` cannot share the list with another value. The [archetype reference](../reference/format-details.md#actors) defines the available roles.
37
37
 
@@ -487,6 +487,93 @@ A page list names this package's own pages. A **stub** — a note with an empty
487
487
 
488
488
  See [page-list details](../reference/format-details.md#page-lists) for the directive's full behaviour.
489
489
 
490
+ ## Theme shortcodes
491
+
492
+ The Hugo theme that renders every site defines four shortcodes. An author writes one in a note body as `{{< name … >}}`, and only the website renders it. The shared note pipeline leaves a shortcode in the source untouched, so the compendium packs and the printed book carry the call as literal text. Use a shortcode only on a page whose purpose is the website. Where a construct must reach all three surfaces, use one of the constructs earlier on this page, which every surface resolves.
493
+
494
+ A shortcode takes either positional arguments or named ones, and a single call cannot mix the two. `{{< glyph "anubis" >}}` and `{{< glyph slug="anubis" frame="tablet" >}}` are both valid; `{{< glyph "anubis" frame="tablet" >}}` is refused by Hugo. A call to a shortcode that has a required argument and omits it fails the site build with the note's position.
495
+
496
+ ### `cartouche`
497
+
498
+ Renders a named SVG seal as an ornamental accent, as a block figure or inline with text. The image is read from `img/cartouches/<slug>.svg` in the consuming site's static files.
499
+
500
+ | Parameter | Value | Default | What it does |
501
+ | --------- | ------------------ | ------------------ | ----------------------------------------------------------------------------------------------------- |
502
+ | `slug` | the SVG's basename | required | Names the file. May be given as the first positional argument when no other parameter is used. |
503
+ | `size` | `inline` | block | `inline` flows the image with the surrounding text. Any other value adds a `cartouche-<value>` class. |
504
+ | `align` | `left` or `right` | centered | Floats the figure so prose wraps around it. |
505
+ | `height` | a CSS length | set by the theme | Sets the image's height, for example `10rem` or `160px`. The width follows the image's aspect ratio. |
506
+ | `alt` | text | the humanized slug | The image's text alternative. |
507
+ | `caption` | text | none | A caption under the image. Block mode only; an inline cartouche ignores it. |
508
+
509
+ ```markdown
510
+ {{< cartouche "takheperu" >}}
511
+
512
+ {{< cartouche slug="aukhelathu" height="10rem" align="right" >}}
513
+
514
+ {{< cartouche slug="anubis" caption="Anpu" >}}
515
+
516
+ The sign {{< cartouche slug="ra" size="inline" >}} stands for the sun.
517
+ ```
518
+
519
+ The first call centers the `takheperu` seal as a block. The second floats the `aukhelathu` seal to the right at ten rem tall, with the following paragraph wrapping around it. The third adds a caption under the seal. The fourth sets the seal in the line of text.
520
+
521
+ ### `exturl`
522
+
523
+ Renders a web address as a code-styled string followed by a button that copies it to the clipboard. It exists for an address a reader is meant to paste somewhere, such as a server address or a manifest URL, where a clickable link would be the wrong affordance. The address is the first positional argument and the shortcode takes no other.
524
+
525
+ | Parameter | Value | Default | What it does |
526
+ | ------------------ | ------------------------- | ------- | ---------------------------------------------------------------------- |
527
+ | first (positional) | the text to show and copy | none | The string displayed in the code element and written to the clipboard. |
528
+
529
+ ```markdown
530
+ Install from {{< exturl "https://example.org/module/manifest.json" >}} in the module browser.
531
+ ```
532
+
533
+ The page shows the address in a code element with a copy icon beside it. Selecting the icon copies the address and briefly marks the button as copied. The text is not a link.
534
+
535
+ ### `glyph`
536
+
537
+ Renders an icon-scale SVG, such as a deity-name cluster, a category icon or an ornamental mark, in the line of text or as a block. The image is `icons/<slug>.svg`, resolved against the consuming site's `params.cdnBaseURL`. This is an image drawn from the site's icon directory; it is a separate construct from the `:icon name:` font glyphs of the [icon guide](../guides/icons.md), which every surface renders from the package's declared `icons` registry.
538
+
539
+ | Parameter | Value | Default | What it does |
540
+ | --------- | ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------- |
541
+ | `slug` | the SVG's basename | required | Names the file. May be given as the first positional argument when no other parameter is used. |
542
+ | `frame` | `tablet` | none | Sets the glyph inside a limestone rectangular frame, so rows of different intrinsic width line up in a table. |
543
+ | `size` | `inline` or `block` | 1.8em tall | `inline` is small and flows with the text. `block` centers the glyph as a standalone line. |
544
+ | `alt` | text | the humanized slug | The image's text alternative. |
545
+
546
+ ```markdown
547
+ | God | Glyph |
548
+ | ------ | ------------------------------------------ |
549
+ | Anubis | {{< glyph slug="anubis" frame="tablet" >}} |
550
+
551
+ The name {{< glyph "ra" >}} is written with one sign.
552
+
553
+ {{< glyph slug="ra" size="block" alt="Sun god Ra" >}}
554
+ ```
555
+
556
+ ### `pagelist`
557
+
558
+ Renders the pages carrying a tag as a list of links, ordered as asked. The group is expressed by intent: a page joins by being tagged and leaves by being untagged. The shortcode lists the site's own pages by the tags the site's taxonomy holds.
559
+
560
+ The ` ```pagelist ` fence described under [Page lists](#page-lists) is a different construct with its own attributes. The fence is answered by the content pipeline and reaches Foundry and the book; the shortcode is the site's alone.
561
+
562
+ | Parameter | Value | Default | What it does |
563
+ | --------- | --------------------------- | ------------- | ----------------------------------------------------------------------------------------------------- |
564
+ | `tag` | a tag | required | The taxonomy term to list, matched case-insensitively. May be given as the first positional argument. |
565
+ | `section` | a top-level section name | every section | Restricts the list to one section of the site, such as `rules`. |
566
+ | `sort` | `title`, `weight` or `date` | `title` | The order of the list. `date` puts the newest first. |
567
+ | `desc` | `true` | off | Shows each page's description beneath its link. Any other value leaves descriptions out. |
568
+
569
+ ```markdown
570
+ {{< pagelist tag="key-concept" >}}
571
+
572
+ {{< pagelist tag="key-concept" section="rules" sort="weight" desc="true" >}}
573
+ ```
574
+
575
+ The first call lists every page tagged `key-concept` alphabetically by title. The second lists only those in the `rules` section, in weight order, each followed by its description. A tag that no page carries produces a build warning naming the tag and the note's position, and the list is left out.
576
+
490
577
  ## Expressions
491
578
 
492
579
  An expression in `{{ }}` is replaced by a value, inline in a sentence. The note's own frontmatter is the context, so a field is read by the path a note writes it at.
package/docs/commands.md CHANGED
@@ -909,7 +909,7 @@ package-build yaml [paths..]
909
909
 
910
910
  **DESCRIPTION**
911
911
 
912
- The check that exists because a duplicate frontmatter key used to parse to
912
+ The check that exists because a duplicate frontmatter key parses to
913
913
  nothing and the note it belonged to silently lost every field, with every
914
914
  later build pass reporting success. Needs no ESLint configuration of its
915
915
  own, so a repository with one for its own source code
@@ -1195,6 +1195,19 @@ Foundry holds its LevelDB packs open and a replacement written underneath it
1195
1195
  corrupts them. `.env.local` and `.env` are loaded first. Reads
1196
1196
  `packageBuild.stageDir`; writes into the stage's data directory.
1197
1197
 
1198
+ Once the new tree is in place, the previous one — renamed aside to
1199
+ `<id>.old-<pid>` — is removed, along with any `<id>.old-<pid>` an earlier
1200
+ deploy left beside the package, whatever its pid. A leftover tree still holds
1201
+ a manifest, so Foundry reads it as a second copy of the package and logs it as
1202
+ an invalid one on every start. Foundry running in a container writes files
1203
+ owned by the container's user, which under rootless Docker is a mapped uid the
1204
+ host user cannot unlink. When the host user is refused, a throwaway container
1205
+ of the stage's image removes the tree as root on the same data root, the
1206
+ mechanism `e2e` uses to hand its install to Foundry. If that fails too — no
1207
+ `docker`, or a remote destination — the deploy still succeeds: it prints a
1208
+ warning for each leftover tree, naming it and the command that removes it,
1209
+ and leaves the tree under its `.old-<pid>` name for the next deploy to sweep.
1210
+
1198
1211
  **OPTIONS**
1199
1212
 
1200
1213
  | Positional | Type | Default | Description |
@@ -1209,7 +1222,8 @@ only).
1209
1222
  **EXIT STATUS**
1210
1223
 
1211
1224
  1 if `FOUNDRYVTT_<STAGE>_DATA` is unset. 1 on any other thrown error.
1212
- Otherwise 0.
1225
+ Otherwise 0, including when the previous tree is left behind with a warning:
1226
+ the new package is installed either way.
1213
1227
 
1214
1228
  **EXAMPLES**
1215
1229
 
@@ -1220,6 +1234,11 @@ package-build: No destination configured for stage 'dev'. Set FOUNDRYVTT_DEV_DAT
1220
1234
  $ FOUNDRYVTT_DEV_DATA=/tmp/pb-docs-demo-410-data package-build deploy dev
1221
1235
  Deploying /private/tmp/pb-docs-demo-410/build/stage → /tmp/pb-docs-demo-410-data/Data/modules/demo-package (local copy)
1222
1236
  Deployed stage 'dev' successfully.
1237
+
1238
+ $ package-build deploy test
1239
+ Deploying /srv/pkg/build/stage → /srv/fvtt-test/Data/systems/sohl (local copy)
1240
+ Deployed stage 'test' successfully.
1241
+ /srv/fvtt-test/Data/systems/sohl.old-4242: warning: the new package is installed, but its previous tree could not be removed (docker was not found on PATH. Install Docker and make sure the `docker` CLI is available.); remove it with `docker run --rm --user 0:0 --volume /srv/fvtt-test:/data --entrypoint rm felddy/foundryvtt:14 -rf /data/Data/systems/sohl.old-4242`
1223
1242
  ```
1224
1243
 
1225
1244
  **SEE ALSO**
@@ -1248,16 +1267,30 @@ first.
1248
1267
 
1249
1268
  `start` requires the stage's data root to exist and runs the image, passing
1250
1269
  through `FOUNDRYVTT_<STAGE>_VERSION`, `FOUNDRYVTT_<STAGE>_WORLD` and
1251
- `FOUNDRYVTT_<STAGE>_LICENSE_KEY` when set. `stop` stops it. `restart` stops
1270
+ `FOUNDRYVTT_<STAGE>_LICENSE_KEY` when set. Every other `FOUNDRY_*` and
1271
+ `CONTAINER_*` variable passes through too, except that `FOUNDRY_USERNAME` and
1272
+ `FOUNDRY_PASSWORD` are withheld when the image has no use for them: the pinned
1273
+ build's `foundryvtt-<version>.zip` is already in the cache (`FOUNDRYVTT_CACHE`,
1274
+ or `container_cache` beneath the data root when that is unset) and a licence
1275
+ is available without the account (a licence key of 24 characters or more, or
1276
+ `Config/license.json` in the data root). Given an account, the image signs in
1277
+ to foundryvtt.com and requests a download URL on every container it creates,
1278
+ cached or not, and a request refused with HTTP 429 stops it before Foundry
1279
+ starts. `stop` stops it. `restart` stops
1252
1280
  it, clears a stale lock left by an unclean shutdown, and starts it again —
1253
1281
  not `docker restart`, which leaves no window to clear the lock. `recreate`
1254
1282
  removes the container first, so a changed world, license or cache
1255
1283
  environment variable actually takes effect, then starts fresh. `rm` removes
1256
1284
  the container. `status` runs `docker ps` filtered to this stage's container
1257
- name. `logs` follows its log. `pull` pulls the configured image
1258
- (`FOUNDRYVTT_CONTAINER_IMAGE`, then `packageBuild.container.image`, then a
1259
- floating `felddy/foundryvtt:release` tag when the package declares no
1260
- `compatibility.minimum`).
1285
+ name. `logs` follows its log. `pull` pulls the configured image.
1286
+
1287
+ The image is `FOUNDRYVTT_CONTAINER_IMAGE`, then `packageBuild.container.image`,
1288
+ then felddy's tag for the pinned build (`felddy/foundryvtt:14.359` for a stage
1289
+ pinned to `14.359`), because each image installs its own build by default and
1290
+ warns that a different `FOUNDRY_VERSION` may not work with it. A stage with no
1291
+ exact pin takes the major tag of its pin or of `compatibility.minimum`
1292
+ (`felddy/foundryvtt:14`), and a package that declares neither floats on
1293
+ `felddy/foundryvtt:release`.
1261
1294
 
1262
1295
  **OPTIONS**
1263
1296
 
@@ -1270,7 +1303,10 @@ floating `felddy/foundryvtt:release` tag when the package declares no
1270
1303
  `FOUNDRYVTT_<STAGE>_PORT`, `FOUNDRYVTT_<STAGE>_VERSION`,
1271
1304
  `FOUNDRYVTT_<STAGE>_WORLD`, `FOUNDRYVTT_<STAGE>_LICENSE_KEY`,
1272
1305
  `FOUNDRYVTT_CONTAINER_IMAGE`, `FOUNDRYVTT_CACHE` (a shared download-cache
1273
- mount).
1306
+ mount), `FOUNDRYVTT_CONTAINER_USER` (passed to `docker run --user`; `0:0`
1307
+ under rootless Docker makes the data root's files belong to the host user,
1308
+ because the image otherwise runs Foundry as user 1000 and ignores
1309
+ `FOUNDRY_UID` and `FOUNDRY_GID`).
1274
1310
 
1275
1311
  **EXIT STATUS**
1276
1312
 
@@ -1455,7 +1491,9 @@ package-build deps fetch [--from <zip|dir>] [--id <id>]
1455
1491
  **DESCRIPTION**
1456
1492
 
1457
1493
  The only action is `fetch`, which fills three caches under `build/cache/`:
1458
- the **content index** of every declared dependency, the **item catalogue or
1494
+ the **content index** of every declared dependency and of every
1495
+ `recommends` or `conflicts` relationship that declares `itemCatalog: true`,
1496
+ the **item catalogue or
1459
1497
  asset archive** of those that additionally declare `itemCatalog: true` or
1460
1498
  `assetArchive: true`, and the **site navigation** —
1461
1499
  `https://www.heroiclands.org/nav.json`, the header menu every package site
@@ -1897,13 +1935,13 @@ $ package-build lint
1897
1935
 
1898
1936
  `package-build links [root]`, `package-build reachability <dir> [file]`,
1899
1937
  `package-build content-format schema`, `package-build content-format fields`,
1900
- `package-build content-format notes`, [Diagnostics](diagnostics.md),
1938
+ [Diagnostics](diagnostics.md),
1901
1939
  [Configuration](configuration.md).
1902
1940
 
1903
1941
  `package-build content-format <action>` checks `docs/content-format.md` —
1904
1942
  the content-format specification this package ships — against reality, in
1905
- three ways that fail for different reasons at different times: `schema`,
1906
- `fields` and `notes`, one section below per action. All three read the
1943
+ two ways that fail for different reasons at different times: `schema`
1944
+ and `fields`, one section below per action. Both read the
1907
1945
  specification named by `--spec`, defaulting to the one this package ships,
1908
1946
  and write nothing.
1909
1947
 
@@ -1949,8 +1987,8 @@ $ package-build content-format schema --schema sohl=schema.json
1949
1987
 
1950
1988
  **SEE ALSO**
1951
1989
 
1952
- `package-build content-format fields`, `package-build content-format notes`,
1953
- `package-build schema`, [Configuration](configuration.md).
1990
+ `package-build content-format fields`, `package-build schema`,
1991
+ [Configuration](configuration.md).
1954
1992
 
1955
1993
  ### `package-build content-format fields`
1956
1994
 
@@ -2004,59 +2042,7 @@ $ package-build content-format fields --fields sohl --coverage
2004
2042
 
2005
2043
  **SEE ALSO**
2006
2044
 
2007
- `package-build content-format schema`, `package-build content-format notes`,
2008
- [Configuration](configuration.md).
2009
-
2010
- ### `package-build content-format notes`
2011
-
2012
- **NAME**
2013
-
2014
- Measure a content tree against the specification's vocabulary.
2015
-
2016
- **SYNOPSIS**
2017
-
2018
- ```
2019
- package-build content-format notes [root] [--strict] [--spec <path>]
2020
- ```
2021
-
2022
- **DESCRIPTION**
2023
-
2024
- Measures a content tree against the vocabulary the specification declares
2025
- per type. A report by default rather than a gate — every authored note
2026
- predates the specification, so a failing check would be red from the day it
2027
- landed and stay red for the length of a migration nobody could act on;
2028
- `--strict` raises its findings to errors, turned on slice by slice as each
2029
- class of finding reaches zero.
2030
-
2031
- **OPTIONS**
2032
-
2033
- | Positional | Type | Default | Description |
2034
- | ---------- | ------ | ------------------------------ | ------------------------ |
2035
- | `root` | string | the configured `paths.content` | Content tree to measure. |
2036
-
2037
- | Option | Type | Default | Description |
2038
- | ---------- | ------- | ---------------------------------------- | ---------------------------------------------------- |
2039
- | `--spec` | string | the shipped `engine/content-format.yaml` | The specification to read. |
2040
- | `--strict` | boolean | `false` | Fail on the findings instead of only reporting them. |
2041
-
2042
- **EXIT STATUS**
2043
-
2044
- 1 if the content index could not be built for any note. Without `--strict`,
2045
- findings never fail the run. With `--strict`, 1 if there are any findings.
2046
- Otherwise 0.
2047
-
2048
- **EXAMPLES**
2049
-
2050
- ```
2051
- $ package-build content-format notes
2052
- […] 0 finding(s) across 4 note(s) measured against docs/content-format.md.
2053
- ```
2054
-
2055
- **SEE ALSO**
2056
-
2057
- `package-build content-format schema`, `package-build content-format fields`,
2058
- `package-build lint [root]`, [Diagnostics](diagnostics.md),
2059
- [Configuration](configuration.md).
2045
+ `package-build content-format schema`, [Configuration](configuration.md).
2060
2046
 
2061
2047
  ### `package-build links [root]`
2062
2048
 
@@ -2073,8 +2059,7 @@ package-build links [root]
2073
2059
  **DESCRIPTION**
2074
2060
 
2075
2061
  Checks that every link in a content tree lands: a dead `#anchor`, a dead
2076
- qualified address, a link with no label (the bare `[[Name]]` alias form is
2077
- retired — every link is an address), a wikilink authored in frontmatter
2062
+ qualified address, a link with no label (every link is an address), a wikilink authored in frontmatter
2078
2063
  (which is data and is never resolved), and a package homepage's markdown
2079
2064
  links and `landing:` addresses, which are published verbatim and use no
2080
2065
  wikilink at all. Also reports a vendored foreign-package content index that
@@ -2192,16 +2177,20 @@ checks Prettier cannot make: a heading level that skips, two sibling
2192
2177
  headings sharing an anchor, a reversed link, an emphasis marker other than
2193
2178
  the one these repositories write. Runs over the repository rather than the
2194
2179
  content tree, taking its rules from this package unless the consumer
2195
- declares its own. `--fix` applies the fixes markdownlint can make. Reads
2196
- every matched file; `--fix` rewrites them.
2197
- Installed dependencies under `node_modules` are excluded even when the
2198
- repository has no `.gitignore` or supplies its own markdownlint ignore list.
2180
+ declares its own. `--fix` applies the fixes markdownlint can make, then
2181
+ reports what remains. Reads every matched file; `--fix` rewrites them.
2182
+ Files excluded by any `.gitignore` are skipped, and `node_modules` and `.git`
2183
+ are never entered, even when the repository supplies its own markdownlint ignore
2184
+ list. A `.markdownlint-cli2.*` options file or `.markdownlint.*` rule file
2185
+ overrides the rules and the ignore list; an option the file cannot mean is
2186
+ refused as `file:line:column: error:` and nothing is linted. See
2187
+ [Project setup](project-setup.md) for the accepted keys.
2199
2188
 
2200
2189
  **OPTIONS**
2201
2190
 
2202
- | Positional | Type | Default | Description |
2203
- | ---------- | --------- | ------------------------------------- | -------------- |
2204
- | `paths` | string(s) | every markdown file in the repository | Globs to lint. |
2191
+ | Positional | Type | Default | Description |
2192
+ | ---------- | --------- | ------------------------------------- | ------------------------------- |
2193
+ | `paths` | string(s) | every markdown file in the repository | Globs, or directories, to lint. |
2205
2194
 
2206
2195
  | Option | Type | Default | Description |
2207
2196
  | ------- | ------- | ------- | -------------------------------------- |
@@ -2308,8 +2297,8 @@ content table, authored where every other page is.
2308
2297
  Three things are written, and nothing outside `build/`:
2309
2298
 
2310
2299
  - `build/hugo/hugo.toml`, generated on every run from `package.json`
2311
- (`homepage`, `description`, `author`), `package-build.config.yaml`
2312
- (`packageBuild.manifest.title`, `site.assets`, `site.notfound`,
2300
+ (`homepage`, `description`), `package-build.config.yaml`
2301
+ (`authors`, `packageBuild.manifest.title`, `site.assets`, `site.notfound`,
2313
2302
  `site.hugo`), the organisation's constants, the shared theme's own
2314
2303
  location, and the navigation `deps fetch` cached. Every value's source is
2315
2304
  listed under
@@ -2363,17 +2352,18 @@ $ package-build site
2363
2352
 
2364
2353
  **NAME**
2365
2354
 
2366
- Build the book the content tree publishes as.
2355
+ Build the books the content tree publishes as.
2367
2356
 
2368
2357
  **SYNOPSIS**
2369
2358
 
2370
2359
  ```
2371
- package-build pdf [--out <path>] [--book-version <version>] [--compile]
2360
+ package-build pdf [--book <id>] [--out <path>] [--book-version <version>] [--compile] [--list-assets]
2372
2361
  ```
2373
2362
 
2374
2363
  **DESCRIPTION**
2375
2364
 
2376
- Builds the book the content tree publishes as — the third surface beside
2365
+ Builds every book `pdf.books` declares, in the order declared, or with
2366
+ `--book <id>` the one book with that `id` — the third surface beside
2377
2367
  `package compile` and `site`, reporting in the same `file:line:column:
2378
2368
  severity: message` shape both of those use. **Not building a book is a
2379
2369
  normal outcome and exits 0**: a package publishing only a homepage, one
@@ -2381,26 +2371,68 @@ with no `pdf:` block, and one with no content tree have each said they
2381
2371
  publish no book, and failing the command over that would break the release
2382
2372
  of every package that is not a book. `--compile` (on by default) runs the
2383
2373
  Typst compiler; `--no-compile` emits the Typst source and stops there,
2384
- which is what lets the source be inspected or compiled by hand. Reads the
2385
- content tree named by `pdf.document` and `paths.content`; writes the
2386
- `.typ` source and, unless `--no-compile`, the `.pdf`, to `--out` or the
2387
- configured `pdf.out`.
2374
+ which is what lets the source be inspected or compiled by hand.
2375
+
2376
+ A reference to a note that another book is home to prints that book and page,
2377
+ `Name (_Book title_, p. 12)`. A compile renders and lays out every book, writes
2378
+ `pages.json` (each note's home book and the page its entry starts on) into the
2379
+ output directory, and renders again with those pages; `--no-compile` runs
2380
+ neither pass and prints `p. ?`. See the
2381
+ [book guide](guides/book.md#a-reference-to-a-note-in-another-book).
2382
+
2383
+ Each book reads its tree from its inline `contents` or the file its
2384
+ `document` names, and the notes under `paths.content`. It writes
2385
+ `<out>-<version>.typ` and, unless `--no-compile`, `<out>-<version>.pdf`, where
2386
+ `<out>` is the book's file stem (default `<contentPackage>-<id>`) and
2387
+ `<version>` is `--book-version`; with no version the files are `<out>.typ` and
2388
+ `<out>.pdf`. Every book is written into `--out`, or the configured `pdf.out`,
2389
+ or `build/dist`.
2390
+
2391
+ A book's `coverImage`, `document` and `front` files are checked first; a
2392
+ missing one is an error at its key in the configuration, and that book is not
2393
+ built while the others are. An `--book` naming no declared book is an error at
2394
+ `pdf.books` that lists the declared ids.
2395
+
2396
+ Every declared book's queries run before any book is written, so coverage is
2397
+ reported over the whole set even with `--book`: a printable note no book selects
2398
+ is a finding at its `type:` line, at the severity `pdf.coverage` names. A book
2399
+ whose queries select no note is an error at `pdf.books[].id` and is not built.
2400
+ A section whose queries select none is left out without a finding. Each book's
2401
+ pictures are staged under a directory named for its file stem beside its
2402
+ source.
2403
+
2404
+ For each book written, the command prints a line of counts and the paths of
2405
+ its source and PDF, each prefixed with the book's `id`. It then prints, per
2406
+ declared book, its entries, distinct notes and notes without a culture, the
2407
+ totals, and one line per note several books select, naming its home book.
2408
+
2409
+ `--list-assets` builds nothing. It prints a JSON array on standard output with
2410
+ one object per declared book, in configuration order — `id`, `title` and the
2411
+ `file`, the path the build writes it to at `--book-version`, relative to the
2412
+ working directory and inside `--out` or the configured `pdf.out` — and an empty
2413
+ array for a package that publishes no book. The release workflow attaches
2414
+ exactly these files and lists them in the release notes.
2388
2415
 
2389
2416
  **OPTIONS**
2390
2417
 
2391
- | Option | Type | Default | Description |
2392
- | ---------------- | ------- | ------------------------ | ------------------------------------------------------------------ |
2393
- | `--out` | string | the configured `pdf.out` | Write the book here instead. |
2394
- | `--book-version` | string | — | Stamp this version on the title page and in the file name. |
2395
- | `--compile` | boolean | `true` | Run the Typst compiler. `--no-compile` emits the source and stops. |
2418
+ | Option | Type | Default | Description |
2419
+ | ---------------- | ------- | ------------------------ | ----------------------------------------------------------------------- |
2420
+ | `--book` | string | every declared book | Build only the book with this `id`. |
2421
+ | `--out` | string | the configured `pdf.out` | Write the books here instead. |
2422
+ | `--book-version` | string | — | Stamp this version on the title page and in each file name. |
2423
+ | `--compile` | boolean | `true` | Run the Typst compiler. `--no-compile` emits the source and stops. |
2424
+ | `--list-assets` | boolean | `false` | Print each book's `id`, `title` and output path as JSON; build nothing. |
2396
2425
 
2397
2426
  **EXIT STATUS**
2398
2427
 
2399
2428
  0 when there is a stated reason not to build (only a homepage note, no `pdf:`
2400
- block, no content tree). 1 if the document tree
2401
- `pdf.document` names cannot be read or parsed, or on any other thrown
2402
- error. Otherwise 0 — findings inside a book that did build (a filter that
2403
- matched nothing, for instance) are reported but never fail the command.
2429
+ block, no content tree). 1 when any finding is an error — an unknown `--book`,
2430
+ a missing `coverImage`, `document` or `front` file, a tree that cannot be read
2431
+ or parsed, a book that selects no note, an uncovered note under
2432
+ `pdf.coverage: error`, a link that resolves nowhere — when a requested book was not
2433
+ written or did not compile, or on any other thrown error. Otherwise 0:
2434
+ warnings, such as a picture the book cannot carry, are reported but do not
2435
+ fail the command.
2404
2436
 
2405
2437
  **EXAMPLES**
2406
2438
 
@@ -2408,8 +2440,14 @@ matched nothing, for instance) are reported but never fail the command.
2408
2440
  $ package-build pdf
2409
2441
  […] No book built: no `pdf:` block is configured, so this package publishes no book
2410
2442
 
2411
- $ package-build pdf --no-compile
2412
- demo-book: error: the document tree named by `pdf.document` cannot be read
2443
+ $ package-build pdf --book nope --no-compile
2444
+ package-build.config.yaml:12:5: error: --book `nope` names no declared book; declared: core, vedyara
2445
+
2446
+ $ package-build pdf --book-version 1.4.0 --no-compile
2447
+ […] core: 1734 entr(ies) and 3 prose file(s) across 41 section(s)
2448
+ […] core: Typst source: build/pdf/thalorna-1.4.0.typ
2449
+ […] vedyara: 28 entr(ies) and 1 prose file(s) across 4 section(s)
2450
+ […] vedyara: Typst source: build/pdf/thalorna-vedyara-1.4.0.typ
2413
2451
  ```
2414
2452
 
2415
2453
  **SEE ALSO**
@@ -2456,9 +2494,10 @@ from them.
2456
2494
 
2457
2495
  A map note is a different thing: it carries a Scene exported from Foundry under
2458
2496
  `data.scene`, on every map subtype. The pack compiler passes it through, binds
2459
- pins marked `#anchor` to pages from the map note's Markdown, and applies
2460
- `data.fixup`, which resolves asset Addresses into selected fields of the
2461
- exported Scene; a missing path or asset is a build error.
2497
+ pins marked `#anchor` to pages from the map note's Markdown, and applies the
2498
+ fixups — `data.fixup` and `data.thumbnail`, the Scene's `thumb` — which resolve
2499
+ asset Addresses into selected fields of the exported Scene; a missing path or
2500
+ asset is a build error.
2462
2501
 
2463
2502
  The four drawings, any of which may be asked for in one run:
2464
2503