@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
@@ -14,48 +14,6 @@ export function makeId(namespace: string, value: string): string;
14
14
  * @returns {boolean} True for a map type.
15
15
  */
16
16
  export function isMapType(type?: string): boolean;
17
- /**
18
- * Throw if `type` names a retired content type.
19
- *
20
- * @param {string} type - The note's declared `type`, or a link's qualifier.
21
- * @param {string} [where] - What carries it — a file path, a link target —
22
- * appended to the message so the reader can go straight to it.
23
- * @throws {Error} Naming the replacement type.
24
- */
25
- export function assertTypeNotRetired(type: string, where?: string): void;
26
- /**
27
- * The current spelling of a content type: itself, or what it was renamed to.
28
- *
29
- * Every table keyed by note type is keyed by the **current** name, and every
30
- * lookup goes through this — which is the whole of the window's behaviour, in
31
- * one function, so no two readers can disagree about which vocabulary a note
32
- * is held to.
33
- *
34
- * A non-string passes through untouched: callers hand this whatever the
35
- * frontmatter carried, and inventing a type for a number would hide the
36
- * missing-`type:` finding that belongs to the linter.
37
- *
38
- * @param {any} type - The type as authored, or as a link spells it.
39
- * @returns {any} The current spelling.
40
- */
41
- export function currentType(type: any): any;
42
- /**
43
- * What a note declaring a renamed type is told, in one place.
44
- *
45
- * Shared by every reporter, because an author meets whichever runs first and
46
- * they should read the same. It names the type to write rather than a value to
47
- * correct — no value makes the retired spelling right — and it says the note
48
- * compiles either way, so a reader knows this is a rename to schedule rather
49
- * than a build to unbreak.
50
- *
51
- * @param {string} retired - The spelling the note used.
52
- * @param {string} current - What to write instead.
53
- * @param {string} [where] - What carries it — a file path, a link target —
54
- * appended to the message. Omit it where the caller emits through a
55
- * diagnostic, whose locator already starts the line.
56
- * @returns {string} The message, unpunctuated at the end as a finding is.
57
- */
58
- export function renamedTypeMessage(retired: string, current: string, where?: string): string;
59
17
  /**
60
18
  * The pack a type's documents live in, in the conventional one-pack-per-type
61
19
  * layout.
@@ -75,9 +33,6 @@ export function renamedTypeMessage(retired: string, current: string, where?: str
75
33
  *
76
34
  * @param {string} type - The target note's `type`.
77
35
  * @returns {{pack: string, docType: string}} The pack and document type.
78
- * @throws {Error} If `type` names a retired content type — see
79
- * {@link RETIRED_TYPES}. The open-set default would otherwise route it to the
80
- * items pack and say nothing.
81
36
  */
82
37
  export function packForType(type: string): {
83
38
  pack: string;
@@ -179,75 +134,6 @@ export const PACK_BY_TYPE: Readonly<Record<string, {
179
134
  pack: string;
180
135
  docType: string;
181
136
  }>>;
182
- /**
183
- * Content types that no longer exist, and what replaced each one.
184
- *
185
- * `character` and `creature` were retired in favour of the single `being` they
186
- * had always compiled into. They are recorded here rather than
187
- * simply deleted because deleting them is the one change that fails *quietly*:
188
- * every type not named in {@link PACK_BY_TYPE} falls through to the open item
189
- * set below, so a note or a link left on the old spelling would be routed to
190
- * the items pack — a wrong answer, arrived at silently, which is exactly the
191
- * failure mode the open-set default exists to avoid for real item types.
192
- *
193
- * Keeping the names lets {@link assertTypeNotRetired} say what happened and
194
- * what to write instead. Entries stay for as long as content in the wild might
195
- * still carry them.
196
- *
197
- * @type {Readonly<Record<string, string>>}
198
- */
199
- export const RETIRED_TYPES: Readonly<Record<string, string>>;
200
- /**
201
- * Content types that were **renamed**, and what each is called now.
202
- *
203
- * A retired type and a renamed one are different cases, and only the first can
204
- * be refused. `character` above was retired *outright* — nothing a note wrote
205
- * made it right, so {@link assertTypeNotRetired} throws. A renamed type has a
206
- * replacement, the two spellings mean the same thing, and a note carrying the
207
- * old one compiles into exactly the document it always did: refusing it would
208
- * fail a build over a note that is not wrong.
209
- *
210
- * So these retire in the three steps `package:` took, and this table is
211
- * the **first**: both spellings resolve, the current one is canonical, and the
212
- * retired one is *reported* — never refused. The sweep of the content trees and
213
- * the refusal come after, once no tree writes the old name. That is the same
214
- * rule `RETIRED_FIELD_ALIASES` states for a renamed *field*, and for the same
215
- * reason: a consumer must be able to adopt the new toolchain before its content
216
- * moves, and there are some 31,000 references to move.
217
- *
218
- * **Why these three, and why this way round.** The specification renamed
219
- * `armorgear`, `concoctiongear` and `projectilegear` to `armor`, `concoction`
220
- * and `projectile`, on the argument that the suffix named the *SoHL document
221
- * subtype* a note compiled into rather than the thing the note is about. That
222
- * rename is **reversed**: the gear-suffixed spellings are the vocabulary, and
223
- * the bare ones are what retires.
224
- *
225
- * Nothing had adopted the bare spellings, which is what makes the reversal
226
- * cheap and the direction obvious: across all five content trees, **every note
227
- * still authors the suffix** — 331 `armorgear` and 18 `projectilegear` in
228
- * `sohl`, 71 `concoctiongear` in `thalorna`, and not one note anywhere writes
229
- * `armor`, `concoction` or `projectile`. The rename produced 349 warnings and
230
- * zero adopters.
231
- *
232
- * `weapongear` never moved, and the reversal restores the consistency its
233
- * staying put had broken: all five gear types spell the suffix again.
234
- *
235
- * **Keyed by the retired spelling**, which is the opposite of
236
- * `RETIRED_FIELD_ALIASES`. The direction follows the operation: a field alias
237
- * is scoped by the schema that declares the *current* name, so that is what a
238
- * reader asks with; a type alias is applied to a value read off a note, so what
239
- * a reader has in hand is the retired spelling and what it needs is the
240
- * replacement.
241
- *
242
- * **The address is not normalised.** A note's canonical address — and therefore
243
- * its document id — carries its `type` as authored, so both spellings keep the
244
- * addresses they already publish and a tree that has not swept compiles
245
- * byte-identically. Renaming the type in a note *is* an address move, which is
246
- * the sweep's business and not this table's.
247
- *
248
- * @type {Readonly<Record<string, string>>}
249
- */
250
- export const RENAMED_TYPES: Readonly<Record<string, string>>;
251
137
  /** Where every other content type compiles: the items pack. */
252
138
  export const ITEM_PACK: Readonly<{
253
139
  pack: "items";
@@ -29,15 +29,17 @@ export function sortKeysDeep(value: unknown): unknown;
29
29
  */
30
30
  export function noteFile(contentBase: string, record: object): string;
31
31
  /**
32
- * The note's own frontmatter, as authored, from an index record.
32
+ * The note's frontmatter from an index record, without the keys the index
33
+ * derives.
33
34
  *
34
35
  * The inverse of the record's spread, and exact rather than best-effort: a
35
36
  * record is the note's frontmatter plus {@link DERIVED_KEYS}, and a note that
36
37
  * authors one of those keys fails the walk — so removing them cannot remove
37
38
  * anything the note wrote. That enforced pairing is what lets a pass read the
38
- * corpus from the index and still lint, route or compile what the *author*
39
- * typed, rather than reasoning about `address:` and `anchors:` as though
40
- * someone had written them.
39
+ * corpus from the index without reasoning about `address:` and `anchors:` as
40
+ * though someone had written them. The record's `data.culture` is the note's
41
+ * resolved culture, so a pass reading the frontmatter sees an inherited culture
42
+ * as well as a written one.
41
43
  *
42
44
  * Lives beside the list it is the inverse of, so the two cannot drift.
43
45
  *
@@ -10,7 +10,6 @@ export * as noteClaims from "./note-claims.mjs";
10
10
  export * as folderNotes from "./folder-notes.mjs";
11
11
  export * as contentPackage from "./content-package.mjs";
12
12
  export * as notePackage from "./note-package.mjs";
13
- export * as retiredFields from "./retired-fields.mjs";
14
13
  export * as runtimeOnlyFields from "./runtime-only-fields.mjs";
15
14
  export * as derivedFields from "./derived-fields.mjs";
16
15
  export * as homepage from "./homepage.mjs";
@@ -62,9 +62,15 @@ export function sectionHasContent(section: object): boolean;
62
62
  * @param {Map<string, string>} links - Address slug → the book's anchor.
63
63
  * @param {(anchor: string) => string} labelFor - The anchor's Typst label.
64
64
  * @param {string} [site] - The absolute address this book's pages are served at.
65
+ * @param {{get: (key: string) => (object|undefined)}} [elsewhere] - The notes
66
+ * another book is home to. One of them is not a link — a PDF link cannot
67
+ * leave its file — and is set as
68
+ * {@link module:engine/pdf-render.crossBookReference}.
65
69
  * @returns {string} Typst markup.
66
70
  */
67
- export function linkToTypst(value: object, links: Map<string, string>, labelFor: (anchor: string) => string, site?: string): string;
71
+ export function linkToTypst(value: object, links: Map<string, string>, labelFor: (anchor: string) => string, site?: string, elsewhere?: {
72
+ get: (key: string) => (object | undefined);
73
+ }): string;
68
74
  /**
69
75
  * Every box as one Typst panel each, breaking between sections.
70
76
  *
@@ -109,7 +109,7 @@ export function valueKindOf(field: object, value: unknown): string;
109
109
  *
110
110
  * The declared string is appended **verbatim**, which is what lets a symbol
111
111
  * hug its number (`160d`) and a word stand off it (`1.1 lbs`). The row's kind
112
- * becomes `text`: a number with a unit on it is no longer a number, and saying
112
+ * becomes `text`: a number with a unit on it is not a number, and saying
113
113
  * otherwise would invite a medium to format it as one.
114
114
  *
115
115
  * @param {string} kind - The row's kind.
@@ -149,7 +149,8 @@ export function linkValue(ref: unknown, resolve: (ref: unknown, hint?: object) =
149
149
  * order, minus what {@link NOTE_FIELD_PRESENTATION} withholds and minus every
150
150
  * field the note left empty. A being's top-level `subType` adds its profile
151
151
  * type. A type that declares no `data:` fields still gets the box, because
152
- * `Name` is a fact about every note.
152
+ * `Name` is a fact about every note. `data.culture` is the resolved culture,
153
+ * inherited ones included, and is left out where it names the note itself.
153
154
  *
154
155
  * @param {object} fm - The note's frontmatter.
155
156
  * @param {object} [options] - Options.
@@ -32,7 +32,7 @@ export function itemDocEntryId(itemId: string): string;
32
32
  * item beats a bare UUID.
33
33
  * @param {string} pageId - The id of the selected entry page, from
34
34
  * {@link journalPageId}.
35
- * @param {object} [fm] - The source note, used to route GM-only prose.
35
+ * @param {object} [fm] - The source note, which routes GM-only prose.
36
36
  * @returns {string} The pointer to store in `system.docHtml`.
37
37
  */
38
38
  export function itemDocPointer(packageId: string, itemId: string, name: string, pageId: string, fm?: object): string;
@@ -5,10 +5,8 @@
5
5
  *
6
6
  * **Derived, never authored.** These are the keys of the consuming
7
7
  * repository's `itemBuilders` registry, so the whitelist and the builder table
8
- * are the same list and cannot drift apart. They already had: `trait` was
9
- * whitelisted long after the item type was retired, with no builder
10
- * behind it, so every `type: trait` note passed the gate and then failed to
11
- * compile.
8
+ * are the same list and cannot drift apart. A whitelist naming a type with no builder would
9
+ * let every note of that type pass the gate and then fail to compile.
12
10
  *
13
11
  * An accessor rather than a hoisted constant, so that importing this module
14
12
  * needs no configuration.
@@ -22,7 +22,7 @@ export function checkLiteratureNote(note: object): object[];
22
22
  /** The `lore` subType whose notes are works of literature. */
23
23
  export const LITERATURE_SUBTYPE: "literature";
24
24
  /**
25
- * The fields a `literature` note may state. Every one is optional.
25
+ * The fields only a `literature` note may state. Every one is optional.
26
26
  *
27
27
  * @type {ReadonlyArray<object>}
28
28
  */
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Lint markdown under a root with the `markdownlint` library.
3
+ *
4
+ * @param {object} params
5
+ * @param {string} params.directory - Repository root.
6
+ * @param {readonly string[]} [params.globs] - Globs selecting the files; when
7
+ * absent, `defaults.globs` plus the root options file's `globs`.
8
+ * @param {object} params.defaults - Options the root options file overrides:
9
+ * `config`, `ignores`, `gitignore` and `globs`.
10
+ * @param {boolean} [params.fix=false] - Repair what markdownlint can, then
11
+ * report what remains.
12
+ * @returns {Promise<{results: object[]}>} markdownlint results, each with its
13
+ * `fileName` relative to `directory`, ordered by file, line and rule.
14
+ * @throws {MarkdownConfigError} A configuration file the contract refuses.
15
+ */
16
+ export function runMarkdownlint(params: {
17
+ directory: string;
18
+ globs?: readonly string[] | undefined;
19
+ defaults: object;
20
+ fix?: boolean | undefined;
21
+ }): Promise<{
22
+ results: object[];
23
+ }>;
24
+ /** Keys an options file may declare at the repository root. */
25
+ export const ROOT_OPTION_KEYS: readonly string[];
26
+ /** A configuration problem, located as far as the source allows. */
27
+ export class MarkdownConfigError extends Error {
28
+ /**
29
+ * @param {string} file - Absolute path of the configuration file.
30
+ * @param {string} message - What is wrong.
31
+ * @param {{line?: number, column?: number}} [where] - Position, when known.
32
+ */
33
+ constructor(file: string, message: string, where?: {
34
+ line?: number;
35
+ column?: number;
36
+ });
37
+ file: string;
38
+ line: number | undefined;
39
+ column: number | undefined;
40
+ }
@@ -9,6 +9,12 @@
9
9
  * and needing no items is the mirror of it. Gating the index on the catalogue
10
10
  * flag would serve neither.
11
11
  *
12
+ * **Includes a relationship of any kind declaring `itemCatalog: true`.** The
13
+ * compile resolves an embedded item through that catalogue however the
14
+ * relationship is declared — a `recommends` entry keeps a module installable
15
+ * under any system and still supplies the items its beings embed — so the lint
16
+ * resolves the same addresses through the same package's index.
17
+ *
12
18
  * **Excludes a relationship declaring `contentIndex: false`.** That opts a
13
19
  * dependency out of both edges at once: it is a Foundry dependency only, cited
14
20
  * by neither a wikilink nor an item reference, so there is nothing here for
@@ -224,7 +230,9 @@ export { metadataFileName };
224
230
  * `conflicts` are declarations *about* other packages rather than dependencies
225
231
  * on them, so an address resolved through one would emit a link into a package
226
232
  * the consumer does not require — a defect in the citing note, not a lookup to
227
- * satisfy.
233
+ * satisfy. A relationship of either kind that declares `itemCatalog: true` is
234
+ * the exception: its items are embedded in this package's own documents, so
235
+ * its index is read whatever its kind (see {@link metadataRelationships}).
228
236
  *
229
237
  * @type {readonly string[]}
230
238
  */
@@ -6,9 +6,19 @@
6
6
  export function addressPositions(fm: object, context?: {}): object[];
7
7
  /**
8
8
  * Decode declared properties at the note boundary. Raw mappings cannot impersonate tuples.
9
+ *
10
+ * A declared Address is read as written. A shortcode spelled with a capital is
11
+ * refused, and every such value in the note is collected before the decode
12
+ * throws, so each reference is reported at its own position: the thrown error
13
+ * describes the first, and its `findings` carry all of them.
14
+ *
9
15
  * @param {object} fm - Parsed YAML or an index record.
10
- * @param {object} context - The builder's package and known types.
16
+ * @param {object} context - The builder's package and known types, and
17
+ * optionally `declaresAddress`, which names the lowercase spelling of a
18
+ * refused shortcode when it is declared.
11
19
  * @returns {object} The same note carrying typed Address properties.
20
+ * @throws {Error} When a declared Address is refused; `keyPath` locates it, and
21
+ * `findings`, where present, lists every refused shortcode.
12
22
  */
13
23
  export function decodeNoteAddresses(fm: object, context: object): object;
14
24
  /**
@@ -25,11 +35,26 @@ export function noteAddressContext(config: object): object;
25
35
  * {@link AddressLink} where it carries an anchor, before the declared
26
36
  * positions are read.
27
37
  *
38
+ * With a `source`, the record is a dependency's, read from its cached index.
39
+ * Every decode failure then leaves as a located finding that names the
40
+ * dependency, the cached file, the record, the field and the remedy, because
41
+ * the shape of a published index is the dependency's to correct and no note in
42
+ * the reading tree is at fault.
43
+ *
28
44
  * @param {object} record - The record.
29
45
  * @param {object} context - Builder defaults.
46
+ * @param {{package: string, file: string, line?: number}} [source] - The
47
+ * dependency and the cached index file the record was read from, with the
48
+ * record's 1-based line when known.
30
49
  * @returns {object} The typed record.
50
+ * @throws {Error} A located finding (`located: true`) when `source` is given
51
+ * and the record cannot be decoded; otherwise the decode error itself.
31
52
  */
32
- export function decodeIndexAddresses(record: object, context: object): object;
53
+ export function decodeIndexAddresses(record: object, context: object, source?: {
54
+ package: string;
55
+ file: string;
56
+ line?: number;
57
+ }): object;
33
58
  /** Structured positions whose Address is nested inside a declared container. */
34
59
  export const STRUCTURED_ADDRESSES: readonly ({
35
60
  path: string[];
@@ -54,7 +54,7 @@ export function noteTypesClaimedBy(docType: string, sources?: ClaimSources): Rea
54
54
  * the answer is the type's full potential — every document such a note *could*
55
55
  * produce — which is what a caller asking about a type rather than a note wants.
56
56
  *
57
- * @param {string} type - The note's declared `type`, current spelling.
57
+ * @param {string} type - The note's declared `type`.
58
58
  * @param {ClaimSources} [sources] - What to answer from.
59
59
  * @param {object} [opts] - Options.
60
60
  * @param {(() => boolean)|boolean} [opts.hasProse] - Whether *this note* carries
@@ -109,12 +109,10 @@ export function noteTypeVocabulary(sources?: ClaimSources): ReadonlySet<string>;
109
109
  * note's type and of a note's systems, and a note can fail either while passing
110
110
  * the other — see {@link discardedSystemBlockFindings}.
111
111
  *
112
- * Read-only: it walks the tree and reports, and writes nothing. Three kinds of
112
+ * Read-only: it walks the tree and reports, and writes nothing. Two kinds of
113
113
  * note are passed over, each for a stated reason rather than by omission — a
114
114
  * file with no frontmatter is not a note; a note with no `type:` is the
115
- * frontmatter linter's finding, which can say what a type is *for*; and a
116
- * retired type is answered by `assertTypeNotRetired` in `ids.mjs`, which names the
117
- * replacement.
115
+ * frontmatter linter's finding, which can say what a type is *for*.
118
116
  *
119
117
  * @param {object} [config] - The resolved build configuration. Defaults to this
120
118
  * repository's.
@@ -17,10 +17,8 @@
17
17
  *
18
18
  * Returns `undefined` for a file with **no address** — no `type`, or no
19
19
  * `shortcode`. Such a file is not an addressable note, so it has no document
20
- * and inventing an id for one would file it under nothing. Every caller already
21
- * had to handle an absent id (that is what the authored field's absence meant),
22
- * so this reports the same thing rather than throwing where a walk used to
23
- * skip; whether an id is *required* stays each pass's own decision
20
+ * and inventing an id for one would file it under nothing. This reports the
21
+ * absence rather than throwing, so a walk skips such a file; whether an id is *required* stays each pass's own decision
24
22
  * (`BasePackCompiler.requiresId`).
25
23
  *
26
24
  * @param {object|null|undefined} fm - Parsed frontmatter.
@@ -9,7 +9,7 @@ export function searchableFrontmatter(fm: object | null | undefined, configured?
9
9
  * Refuse a note that declares `package:` at all.
10
10
  *
11
11
  * Presence is the whole test — a declaration that *agrees* with the
12
- * configuration is as retired as one that disagrees, and an empty one
12
+ * configuration is refused like one that disagrees, and an empty one
13
13
  * (`package:`, which parses as `null`) is still the field.
14
14
  *
15
15
  * @param {object|null|undefined} fm - Parsed frontmatter, or nothing when it
@@ -13,9 +13,7 @@
13
13
  * either state.
14
14
  *
15
15
  * An HTML comment is deliberately not empty. An invisible marker deciding
16
- * whether a page exists is the failure the retired `draft:` field was removed
17
- * for, and reintroducing it in comment form would be worse, because nothing
18
- * would report it.
16
+ * whether a page exists would be a failure nothing reports.
19
17
  *
20
18
  * @param {string|null|undefined} body - The note body, frontmatter stripped.
21
19
  * @returns {boolean} Whether the body is empty.
@@ -65,11 +65,8 @@ export function isGmNote(fm: any): boolean;
65
65
  * What a note carrying a subType outside the address charset is told.
66
66
  *
67
67
  * **Why the charset holds for a subType, which reaches no address.** The rule
68
- * said
69
- * "the hyphen separates the segments of an address", and that was true of a
70
- * subType when it shipped: `sectionOf` returned a `doc`'s subType, so the value
71
- * was a URL path segment. Sections are retired and it is not one. The rule
72
- * stays, on its own footing: a subType is a vocabulary term the whole toolchain
68
+ * says "the hyphen separates the segments of an address", and a subType is not
69
+ * one. The rule stands on its own footing: a subType is a vocabulary term the whole toolchain
73
70
  * keys on, and it is one closed set away from being an address segment again —
74
71
  * so the reason to spell it in the address charset is that a charset holding
75
72
  * for a type, a shortcode and a `contentPackage` but not for a subType is a
@@ -99,7 +96,7 @@ export function typeCharsetMessage(type: string): string;
99
96
  *
100
97
  * The message states the reason **per key**, as {@link typeCharsetMessage} and
101
98
  * {@link subTypeCharsetMessage} do: a type is an address segment, and a subType
102
- * is not one, since sections are retired, so a single claim covering both
99
+ * is not one, so a single claim covering both
103
100
  * would be half wrong.
104
101
  *
105
102
  * @param {Readonly<Record<string, TypeVocabulary>>} vocabulary - The registry.
@@ -208,13 +205,10 @@ export const NOTE_TOP_LEVEL_KEY_SET: ReadonlySet<string>;
208
205
  * row in twenty-five tables would be twenty-five chances for one of them to
209
206
  * disagree with the rest.
210
207
  *
211
- * Both are art slots, and they are legal everywhere for different reasons.
212
- * `icon` is the document's profile art and most types compile into a document
213
- * that carries one; where a type's passes emit none, the frontmatter lint says
214
- * so as a warning rather than the vocabulary refusing the key, because the
215
- * value may still be read by a page template. `banner` reaches no compiled
216
- * document at all — it is the page's hero image — and a page is what every note
217
- * publishes.
208
+ * `banner` is an art slot legal everywhere: it reaches no compiled document, it
209
+ * is the page's hero image, and a page is what every note publishes. `icon` is
210
+ * not shared; it is declared by {@link ICON_FIELD} for the types whose
211
+ * document carries art.
218
212
  *
219
213
  * @type {readonly DataFieldSpec[]}
220
214
  */
@@ -228,9 +222,7 @@ export const SHARED_DATA_FIELDS: readonly DataFieldSpec[];
228
222
  *
229
223
  * It is a **presentation** fact and nothing more. A draft note compiles,
230
224
  * validates, publishes and resolves like any other; only a link *into* it
231
- * renders marked. What it emphatically is not is the retired `draft:` field,
232
- * whose entire effect was to move a note from published to unresolvable — see
233
- * {@link draftRetiredMessage}.
225
+ * renders marked.
234
226
  */
235
227
  export const DRAFT_TAG: "draft";
236
228
  /** A note intended for the GM rather than the player-facing outputs. */
@@ -362,16 +354,13 @@ export type DataFieldSpec = {
362
354
  * value — which is the honest answer wherever the specification's stated
363
355
  * shape and the shape notes are authored in today disagree.
364
356
  */
365
- kind?: "string" | "number" | "boolean" | "map" | "address" | "list" | "shortcode" | "list-or-map" | "scalar-or-map" | undefined;
357
+ kind?: "string" | "number" | "boolean" | "map" | "address" | "list" | "scalar-or-map" | "shortcode" | undefined;
366
358
  /**
367
359
  * - Type of each list or pack-map value.
368
360
  */
369
361
  entryKind?: "address" | "shortcode" | undefined;
370
362
  /**
371
- * - Type of each map key. On a
372
- * `list-or-map` field it describes the map form alone, and `entryKind` the
373
- * list form alone: reading both of one value would check a map's entries as
374
- * if they were its keys.
363
+ * - Type of each map key.
375
364
  */
376
365
  keyKind?: "address" | "shortcode" | undefined;
377
366
  /**
@@ -393,6 +382,12 @@ export type DataFieldSpec = {
393
382
  * - Allowed Address types, separate from `ref`.
394
383
  */
395
384
  accepts?: readonly string[] | undefined;
385
+ /**
386
+ * - The literal value that declines the art this
387
+ * Address field names. It is not an Address, is never resolved, and is not
388
+ * reported as unresolved.
389
+ */
390
+ declines?: string | undefined;
396
391
  /**
397
392
  * - The anchor kinds an
398
393
  * Address here may name with `#<anchor>`. Absent, an anchor is refused.
@@ -426,6 +421,13 @@ export type DataFieldSpec = {
426
421
  * ({@link module :engine/field-spec}), which names a **Shortcode** instead.
427
422
  */
428
423
  ref?: string | undefined;
424
+ /**
425
+ * - On a map, the exported Scene property an
426
+ * Address field's resolved path is written into, as a `data.fixup` path. The
427
+ * Scene compile applies the field as one more fixup — see
428
+ * {@link module :engine/exported-scene}.
429
+ */
430
+ sceneProperty?: string | undefined;
429
431
  /**
430
432
  * - For a `scalar-or-map` field, what its keys name.
431
433
  * `"pack"` means each is a pack this package declares, so a key naming none
@@ -460,6 +462,11 @@ export type DataFieldSpec = {
460
462
  * is, where the keys are data rather than declared names.
461
463
  */
462
464
  values?: object | undefined;
465
+ /**
466
+ * - The closed set a present string
467
+ * value is one of; any other word is a finding at the value.
468
+ */
469
+ allowed?: readonly string[] | undefined;
463
470
  /**
464
471
  * - One line, for the author-facing reference.
465
472
  */
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * **Two of them in one directory is an error.** Picking one by precedence would
5
5
  * mean a repository mid-conversion silently builds from the file its author is
6
- * no longer editing, and the build would look entirely healthy while doing it.
6
+ * not editing, and the build would look entirely healthy while doing it.
7
7
  * The walk continues past a directory holding none, so a repository may still
8
8
  * sit inside one that has its own.
9
9
  *
@@ -137,11 +137,7 @@ export const CONFIG_BASENAME: "package-build.config";
137
137
  * the order the names are *reported* in, which is the order a reader should
138
138
  * reach for them: YAML first, `.mjs` last.
139
139
  *
140
- * The `package-build.config.*` stem this package read before the two toolchains
141
- * merged is **not** resolved. 3.0.0 renames the file rather than accepting both:
142
- * a deprecation window here would mean a repository could sit indefinitely on a
143
- * name for a package that no longer exists, and the upgrade already requires
144
- * touching the consumer's manifest and imports.
140
+ * Only the `package-build.config.*` stem is resolved.
145
141
  *
146
142
  * @type {readonly string[]}
147
143
  */
@@ -62,10 +62,10 @@ export class PackRoutingError extends Error {
62
62
  /**
63
63
  * The frontmatter field a note declares its pack in.
64
64
  *
65
- * Deliberately close to the retired `package:` and deliberately not the same
66
- * word: `package:` said which *distribution* owned a note — now the
67
- * repository's `contentPackage`, and no longer authorable — while `pack:`
68
- * says which *compendium* receives its document.
65
+ * Deliberately close to `package:` and deliberately not the same word:
66
+ * `package:` would say which *distribution* owns a note — that is the
67
+ * repository's `contentPackage`, and not authorable — while `pack:` says which
68
+ * *compendium* receives its document.
69
69
  */
70
70
  export const PACK_FIELD: "pack";
71
71
  export { NO_PACK };
@@ -138,10 +138,10 @@ export type PathnameForms = {
138
138
  */
139
139
  authored: string;
140
140
  /**
141
- * `blank` for `""`, `external`
142
- * for an address no package owns, `package` for an owned file.
141
+ * `external` for an address no
142
+ * package owns, `package` for an owned file.
143
143
  */
144
- state: "blank" | "external" | "package";
144
+ state: "external" | "package";
145
145
  /**
146
146
  * The content package that owns the file, or
147
147
  * `null` when no package does.