@heroiclands/package-build 20.3.0 → 20.4.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 (178) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/CONTENT.md +111 -92
  3. package/README.md +2 -9
  4. package/bin/content-build.mjs +70 -61
  5. package/bin/package-build.mjs +2 -2
  6. package/config.mjs +6 -8
  7. package/content-config.mjs +187 -64
  8. package/docs/content-format.md +57 -68
  9. package/e2e.mjs +4 -4
  10. package/engine/actor-compiler.mjs +36 -36
  11. package/engine/address-charset.mjs +6 -6
  12. package/engine/address-diff.mjs +12 -11
  13. package/engine/anchored-sections.mjs +1 -1
  14. package/engine/anchors.mjs +1 -1
  15. package/engine/base-compiler.mjs +36 -38
  16. package/engine/bundle-notes.mjs +4 -4
  17. package/engine/bundles.mjs +8 -8
  18. package/engine/code-fences.mjs +3 -3
  19. package/engine/compendiums.mjs +8 -8
  20. package/engine/compile-corpus.mjs +4 -5
  21. package/engine/content-address.mjs +15 -15
  22. package/engine/content-charset.mjs +1 -1
  23. package/engine/content-format-check.mjs +24 -25
  24. package/engine/content-format.mjs +8 -8
  25. package/engine/content-html.mjs +202 -0
  26. package/engine/content-icons.mjs +225 -98
  27. package/engine/content-index.mjs +17 -17
  28. package/engine/content-links.mjs +30 -30
  29. package/engine/content-lint.mjs +20 -24
  30. package/engine/content-package.mjs +7 -9
  31. package/engine/content-slug.mjs +2 -2
  32. package/engine/content-tables.mjs +7 -8
  33. package/engine/content-tree.mjs +1 -1
  34. package/engine/derived-fields.mjs +174 -0
  35. package/engine/diagnostics.mjs +3 -4
  36. package/engine/document-subtypes.mjs +6 -7
  37. package/engine/field-reference.mjs +8 -8
  38. package/engine/field-spec.mjs +29 -30
  39. package/engine/folder-notes.mjs +14 -14
  40. package/engine/foreign-catalog.mjs +8 -9
  41. package/engine/foundry-entries.mjs +19 -20
  42. package/engine/frontmatter-lint.mjs +64 -69
  43. package/engine/frontmatter.mjs +11 -11
  44. package/engine/generate.mjs +39 -39
  45. package/engine/helpers.mjs +63 -54
  46. package/engine/homepage.mjs +20 -22
  47. package/engine/ids.mjs +15 -15
  48. package/engine/index-records.mjs +4 -4
  49. package/engine/index.mjs +14 -8
  50. package/engine/item-compiler.mjs +54 -29
  51. package/engine/item-docs.mjs +11 -13
  52. package/engine/item-registry.mjs +13 -13
  53. package/engine/journals.mjs +15 -15
  54. package/engine/macros.mjs +3 -3
  55. package/engine/map-notes.mjs +6 -6
  56. package/engine/metadata-index.mjs +5 -5
  57. package/engine/note-claims.mjs +36 -34
  58. package/engine/note-ids.mjs +5 -5
  59. package/engine/note-package.mjs +4 -4
  60. package/engine/note-renames.mjs +3 -3
  61. package/engine/note-schemas.mjs +3 -3
  62. package/engine/note-vocabulary.mjs +27 -26
  63. package/engine/pack-config.mjs +13 -13
  64. package/engine/pack-router.mjs +0 -0
  65. package/engine/prose-config.mjs +9 -9
  66. package/engine/prose-lint.mjs +12 -13
  67. package/engine/region-events.mjs +2 -2
  68. package/engine/retired-fields.mjs +24 -22
  69. package/engine/runtime-only-fields.mjs +1 -1
  70. package/engine/scene-levels.mjs +1 -1
  71. package/engine/scenes.mjs +19 -20
  72. package/engine/schema-check.mjs +20 -20
  73. package/engine/schema-extract.mjs +1 -1
  74. package/engine/site-build.mjs +45 -47
  75. package/engine/site-index.mjs +17 -18
  76. package/engine/sql-tables.mjs +21 -13
  77. package/engine/subtype-registry.mjs +5 -4
  78. package/engine/system-block.mjs +18 -19
  79. package/engine/systems.mjs +2 -2
  80. package/engine/web-wikilinks.mjs +35 -35
  81. package/engine/wikilink-syntax.mjs +16 -16
  82. package/engine/wikilinks.mjs +39 -39
  83. package/hm3/actors.mjs +22 -22
  84. package/hm3/document-subtypes.mjs +5 -5
  85. package/hm3/index.mjs +2 -2
  86. package/hm3/item-builders.mjs +1 -1
  87. package/hm3/item-fields.mjs +3 -3
  88. package/hm3/items.mjs +20 -8
  89. package/hm3/template-priority.mjs +2 -2
  90. package/lang.mjs +3 -3
  91. package/manifest.mjs +17 -20
  92. package/package.json +1 -2
  93. package/release.mjs +3 -3
  94. package/sohl/actors.mjs +28 -28
  95. package/sohl/affiliation-standings.mjs +2 -2
  96. package/sohl/being-info.mjs +5 -5
  97. package/sohl/default-item-art.mjs +5 -5
  98. package/sohl/document-subtypes.mjs +5 -5
  99. package/sohl/index.mjs +3 -3
  100. package/sohl/item-builders.mjs +7 -7
  101. package/sohl/item-fields.mjs +8 -9
  102. package/sohl/items.mjs +20 -6
  103. package/sohl/kb-passes.mjs +5 -5
  104. package/sohl/note-schemas.mjs +7 -7
  105. package/sohl/skill-base.mjs +1 -1
  106. package/types/content-config.d.mts +25 -24
  107. package/types/e2e.d.mts +3 -3
  108. package/types/engine/actor-compiler.d.mts +14 -13
  109. package/types/engine/address-charset.d.mts +6 -6
  110. package/types/engine/address-diff.d.mts +3 -3
  111. package/types/engine/base-compiler.d.mts +17 -19
  112. package/types/engine/bundle-notes.d.mts +3 -3
  113. package/types/engine/bundles.d.mts +1 -1
  114. package/types/engine/code-fences.d.mts +3 -3
  115. package/types/engine/compendiums.d.mts +3 -3
  116. package/types/engine/compile-corpus.d.mts +1 -1
  117. package/types/engine/content-address.d.mts +11 -11
  118. package/types/engine/content-format-check.d.mts +6 -6
  119. package/types/engine/content-format.d.mts +2 -2
  120. package/types/engine/content-html.d.mts +78 -0
  121. package/types/engine/content-icons.d.mts +150 -62
  122. package/types/engine/content-index.d.mts +2 -2
  123. package/types/engine/content-links.d.mts +8 -8
  124. package/types/engine/content-lint.d.mts +2 -2
  125. package/types/engine/content-package.d.mts +6 -8
  126. package/types/engine/derived-fields.d.mts +101 -0
  127. package/types/engine/diagnostics.d.mts +2 -2
  128. package/types/engine/document-subtypes.d.mts +3 -3
  129. package/types/engine/field-spec.d.mts +37 -39
  130. package/types/engine/folder-notes.d.mts +4 -5
  131. package/types/engine/foreign-catalog.d.mts +3 -3
  132. package/types/engine/foundry-entries.d.mts +6 -7
  133. package/types/engine/frontmatter-lint.d.mts +7 -7
  134. package/types/engine/frontmatter.d.mts +7 -7
  135. package/types/engine/generate.d.mts +7 -7
  136. package/types/engine/helpers.d.mts +25 -27
  137. package/types/engine/homepage.d.mts +16 -18
  138. package/types/engine/ids.d.mts +13 -13
  139. package/types/engine/index-records.d.mts +3 -3
  140. package/types/engine/index.d.mts +2 -0
  141. package/types/engine/item-compiler.d.mts +21 -5
  142. package/types/engine/item-docs.d.mts +2 -2
  143. package/types/engine/item-registry.d.mts +6 -6
  144. package/types/engine/journals.d.mts +3 -3
  145. package/types/engine/map-notes.d.mts +2 -2
  146. package/types/engine/metadata-index.d.mts +4 -4
  147. package/types/engine/note-claims.d.mts +16 -15
  148. package/types/engine/note-ids.d.mts +4 -4
  149. package/types/engine/note-package.d.mts +1 -1
  150. package/types/engine/note-renames.d.mts +3 -3
  151. package/types/engine/note-vocabulary.d.mts +9 -8
  152. package/types/engine/pack-config.d.mts +3 -3
  153. package/types/engine/pack-router.d.mts +1 -1
  154. package/types/engine/prose-config.d.mts +9 -9
  155. package/types/engine/prose-lint.d.mts +3 -4
  156. package/types/engine/region-events.d.mts +2 -2
  157. package/types/engine/retired-fields.d.mts +10 -9
  158. package/types/engine/schema-check.d.mts +11 -11
  159. package/types/engine/site-build.d.mts +18 -19
  160. package/types/engine/site-index.d.mts +3 -4
  161. package/types/engine/sql-tables.d.mts +11 -5
  162. package/types/engine/subtype-registry.d.mts +3 -3
  163. package/types/engine/system-block.d.mts +3 -3
  164. package/types/engine/web-wikilinks.d.mts +7 -7
  165. package/types/engine/wikilink-syntax.d.mts +17 -17
  166. package/types/engine/wikilinks.d.mts +13 -12
  167. package/types/hm3/actors.d.mts +1 -1
  168. package/types/hm3/document-subtypes.d.mts +1 -1
  169. package/types/hm3/items.d.mts +1 -2
  170. package/types/hm3/template-priority.d.mts +1 -1
  171. package/types/manifest.d.mts +8 -8
  172. package/types/sohl/actors.d.mts +2 -2
  173. package/types/sohl/affiliation-standings.d.mts +2 -2
  174. package/types/sohl/being-info.d.mts +2 -2
  175. package/types/sohl/document-subtypes.d.mts +1 -1
  176. package/types/sohl/items.d.mts +1 -2
  177. package/types/sohl/note-schemas.d.mts +1 -1
  178. package/MIGRATING.md +0 -608
@@ -17,15 +17,15 @@
17
17
  *
18
18
  * Both are read from the one resolved configuration, so they are literally the
19
19
  * same object's keys and values: a type cannot be whitelisted for compilation
20
- * without the builder that compiles it, which is the guarantee #1504 exists
21
- * for. Where a consumer declares **several** registries, one per system (#58),
20
+ * without the builder that compiles it, which is the guarantee this exists
21
+ * for. Where a consumer declares **several** registries, one per system,
22
22
  * the vocabulary is their union and every lookup below takes the system that is
23
23
  * asking — a type both systems declare has two builders, and answering with one
24
24
  * of them because it was declared first is the silent-wrong-output failure this
25
25
  * package spends its time removing. The Item compiler dispatches through {@link itemBuilder}, so the table a
26
26
  * consumer configured is the table its notes compile with — the whitelist and
27
- * the dispatch used to come from different places, and a consumer supplying its
28
- * own registry got the types it asked for and the builders it did not (#1563).
27
+ * the dispatch would otherwise come from different places, and a consumer supplying its
28
+ * own registry got the types it asked for and the builders it did not.
29
29
  *
30
30
  * **The registry itself is a consumer's, and stays a leaf.** SoHL's lives in
31
31
  * `@heroiclands/package-build/sohl/item-builders`; the consumer names it in
@@ -52,9 +52,9 @@ import { resolveImg } from "./helpers.mjs";
52
52
  * **Derived, never authored.** These are the keys of the consuming
53
53
  * repository's `itemBuilders` registry, so the whitelist and the builder table
54
54
  * are the same list and cannot drift apart. They already had: `trait` was
55
- * whitelisted long after the item type was retired (#651), with no builder
55
+ * whitelisted long after the item type was retired, with no builder
56
56
  * behind it, so every `type: trait` note passed the gate and then failed to
57
- * compile (#1504).
57
+ * compile.
58
58
  *
59
59
  * An accessor rather than a hoisted constant, so that importing this module
60
60
  * needs no configuration (#2).
@@ -73,7 +73,7 @@ export function itemTypes() {
73
73
  * two systems declare the type. So a contested type without a system **throws**
74
74
  * rather than resolving: the alternative is a document built by one system's
75
75
  * builder and stamped with another's, which is exactly the shape of defect the
76
- * document-subtype map exists to stop (#79).
76
+ * document-subtype map exists to stop.
77
77
  *
78
78
  * @param {string} what - What is being looked up, for the message.
79
79
  * @param {"itemBuilders"|"itemArt"|"itemFields"} table - Which table.
@@ -87,7 +87,7 @@ function lookup(what, table, type, system) {
87
87
  // The registry is keyed by the current spelling of a note type, and a note
88
88
  // still on a renamed one resolves through it unchanged. One place, because
89
89
  // every type-keyed table this module reaches — builders, art, fields — is
90
- // indexed here (#78).
90
+ // indexed here.
91
91
  type = currentType(type);
92
92
  if (system !== undefined) {
93
93
  const perSystem = /** @type {Record<string, Record<string, unknown>>} */ (
@@ -112,7 +112,7 @@ function lookup(what, table, type, system) {
112
112
  *
113
113
  * Unreachable through the compiler — its whitelist *is* this registry's keys —
114
114
  * so a throw here means a caller invented a type. It names the type rather than
115
- * failing as an anonymous `is not a function` (#1504).
115
+ * failing as an anonymous `is not a function`.
116
116
  *
117
117
  * @param {string} type - The note's `type` frontmatter.
118
118
  * @param {string} [system] - The system compiling it, where a build declares
@@ -139,9 +139,9 @@ export function itemBuilder(type, system) {
139
139
  * The frontmatter fields a type's registry entry declares, if any.
140
140
  *
141
141
  * Sparse by design: a type whose entry declares none compiles normally and is
142
- * simply undocumented (#22). What reads it is the `system`-block passthrough,
142
+ * simply undocumented. What reads it is the `system`-block passthrough,
143
143
  * which has to know which paths a declared field already writes before it
144
- * writes the rest (#58).
144
+ * writes the rest.
145
145
  *
146
146
  * @param {string} type - The item type.
147
147
  * @param {string} [system] - The system compiling it, where a build declares
@@ -160,7 +160,7 @@ export function itemFields(type, system) {
160
160
  *
161
161
  * Read from the consuming repository's `itemBuilders` registry, the same place
162
162
  * the type itself is declared, so a consumer's own type can bring art a
163
- * SoHL-owned table could never hold. Art used to be looked up in
163
+ * SoHL-owned table could never hold. Art is not looked up in
164
164
  * `sohl/default-item-art.mjs` instead: a type was configurable while its
165
165
  * default art was not, so a second consumer's items compiled only if every one
166
166
  * of its notes set `img:` (#7).
@@ -195,7 +195,7 @@ export function itemArt(type, system) {
195
195
  );
196
196
  }
197
197
  // `art` is non-empty by the guard above, so the translation never returns
198
- // the `null` a note's unset `img:` would (#218); the coalesce states that
198
+ // the `null` a note's unset `img:` would; the coalesce states that
199
199
  // rather than leaving the declared `string` return a half-truth.
200
200
  return resolveImg(art) ?? "";
201
201
  }
@@ -28,13 +28,13 @@
28
28
  * keeps only a pointer to it. See `item-docs.mjs` for why, and for the ids the
29
29
  * two passes agree on. A macro note's `{#script}` page is compiled here like
30
30
  * any other: the macro pass reads the same page independently, and withholds
31
- * nothing from the journal (#1514).
31
+ * nothing from the journal.
32
32
  *
33
33
  * Folder placement is identical to the items pack: `sohl.packFolder` in
34
34
  * frontmatter is a folder **note's address**, resolved through the shared
35
- * address index by the constructor's `folderResolver` (#255, #260). A folder
35
+ * address index by the constructor's `folderResolver`. A folder
36
36
  * materialises in every pack holding a document that names it, so a journals
37
- * pack needs to declare nothing (#257) — which is what stopped this pass
37
+ * pack needs to declare nothing — which is what stopped this pass
38
38
  * filing documentation into folders its own pack had never heard of. A
39
39
  * documentation entry reuses its document's folder verbatim.
40
40
  *
@@ -43,7 +43,7 @@
43
43
  *
44
44
  * The walk itself — filtering by type, expanding tables, converting
45
45
  * wikilinks, writing the JSON and counting errors — belongs to {@link sohl.utils.packs.BasePackCompiler}; this module
46
- * states only what makes this pass its own (#1509).
46
+ * states only what makes this pass its own.
47
47
  */
48
48
 
49
49
  import log from "loglevel";
@@ -141,7 +141,7 @@ export function splitPages(body, leadName = "Introduction") {
141
141
  * on an identity alone:
142
142
  *
143
143
  * - **An anchor**, declared twice, has always collided.
144
- * - **A name**, repeated among the unanchored pages, collides since #268 took
144
+ * - **A name**, repeated among the unanchored pages, collides once
145
145
  * the index out of the key. `MD024` with `siblings_only` already makes two
146
146
  * sibling headings with the same text a lint error, so this is the same rule
147
147
  * restated where the build can enforce it — a lint is a separate command, and
@@ -199,7 +199,7 @@ export function assertUniqueAnchors(rawPages, noteName) {
199
199
  * doc's first page without having compiled it (see
200
200
  * {@link sohl.utils.packs.itemDocPointer}).
201
201
  *
202
- * **It takes no index** (#268). A page used to be keyed by position *and* name,
202
+ * **It takes no index**. Keying a page by position *and* name,
203
203
  * so inserting a heading renumbered every page after it and a re-import created
204
204
  * new pages beside the old ones — while nothing about those pages had changed.
205
205
  * The anchored case above never took one, and is the shape this now shares.
@@ -275,7 +275,7 @@ export function buildPages(rawPages, entryId, noteName) {
275
275
  * @param {object} [params.stats] - The `_stats` block to stamp. Passed by the
276
276
  * caller because it is a property of the *pack* being written, not of the
277
277
  * entry: a module may ship the same content for two systems, and each pack's
278
- * documents record the system version they were built against (#48). A
278
+ * documents record the system version they were built against. A
279
279
  * caller with no pack in hand gets the package-wide block.
280
280
  * @returns {object} The JournalEntry document, keyed for the pack.
281
281
  */
@@ -317,7 +317,7 @@ export class Journals extends BasePackCompiler {
317
317
  /**
318
318
  * **None.** A JournalEntry has no artwork — no `img` property, and no
319
319
  * nested place for one — so a note whose whole document is prose has
320
- * nowhere to put an authored path (#349).
320
+ * nowhere to put an authored path.
321
321
  *
322
322
  * The emptiness is the declaration, in the sense `JOURNAL_ONLY_FIELDS` is:
323
323
  * it is what separates a pass that emits no art from one that has simply
@@ -338,13 +338,13 @@ export class Journals extends BasePackCompiler {
338
338
 
339
339
  /**
340
340
  * Journal notes, plus every doc-carrying note — an item's prose is its
341
- * documentation, so it compiles here and the item keeps a pointer to it
342
- * (#1348); a macro's is the same arrangement (#1514), and so is a map's,
343
- * whose prose is the place description its pins point at (#1525).
341
+ * documentation, so it compiles here and the item keeps a pointer to it;
342
+ * a macro's is the same arrangement, and so is a map's,
343
+ * whose prose is the place description its pins point at.
344
344
  *
345
345
  * Two memberships, and they mean different things.
346
346
  * {@link module:engine/ids.JOURNAL_TYPES} is the types whose whole document
347
- * *is* a journal — `doc`, and since #241 `place`, `lore` and `scenario`,
347
+ * *is* a journal — `doc`, `place`, `lore` and `scenario`,
348
348
  * which the content format has always described and nothing compiled.
349
349
  * {@link sohl.utils.packs.docEntryTypes}, read through
350
350
  * {@link sohl.utils.packs.hasDocEntry}, is the types whose prose becomes a
@@ -383,7 +383,7 @@ export class Journals extends BasePackCompiler {
383
383
  * the items pass wrote resolves to it (see
384
384
  * {@link sohl.utils.packs.itemDocPointer}). A macro's `{#script}` page is
385
385
  * compiled here like any other; nothing is withheld from the journal
386
- * because the macro pass also reads it (#1514).
386
+ * because the macro pass also reads it.
387
387
  *
388
388
  * @param {object} fm - The note's frontmatter.
389
389
  * @param {string} markdown - The body, tables expanded and wikilinks
@@ -404,12 +404,12 @@ export class Journals extends BasePackCompiler {
404
404
  // resolving it *here* is what cures the defect this comment used to
405
405
  // describe. A folder note has one definition and one address, so the
406
406
  // journals pack materialises the very folder the items pack does, by
407
- // the same id (#257). There is no second folder file left to disagree
407
+ // the same id. There is no second folder file left to disagree
408
408
  // with the first, and so no arrangement to assume: the mirroring
409
409
  // failure is unrepresentable rather than merely reported.
410
410
  //
411
411
  // The id spelling used to cross packs verbatim here, on the assumption
412
- // both declared it — the arrangement #260 retires with the YAML.
412
+ // both declared it — the arrangement retired with the YAML.
413
413
  const { value: authoredFolder } = folderField(fm);
414
414
  const folder = this.folderResolver(authoredFolder, { isAddress: true });
415
415
 
package/engine/macros.mjs CHANGED
@@ -46,7 +46,7 @@
46
46
  *
47
47
  * The walk itself — filtering by type, expanding tables, converting
48
48
  * wikilinks, writing the JSON and counting errors — belongs to {@link sohl.utils.packs.BasePackCompiler}; this module
49
- * states only what makes this pass its own (#1509).
49
+ * states only what makes this pass its own.
50
50
  */
51
51
 
52
52
  import log from "loglevel";
@@ -261,7 +261,7 @@ export function buildMacroEntry(fm, { command, folder = null, stats = defaultSta
261
261
  // into chat instead of running.
262
262
  type: resolveMacroType(fm, name),
263
263
  author: null,
264
- // Nullish, not `||` (#218): a macro note that names no art gets the
264
+ // Nullish, not `||`: a macro note that names no art gets the
265
265
  // shared default, one that writes `img: ""` ships blank on purpose.
266
266
  img: resolveImg(fm.img) ?? DEFAULT_MACRO_IMG,
267
267
  scope: resolveMacroScope(fm, name),
@@ -326,7 +326,7 @@ export class Macros extends BasePackCompiler {
326
326
  folder: this.folderResolver(folderField(fm).value, {
327
327
  isAddress: folderField(fm).isAddress,
328
328
  }),
329
- // This pack's system, not the package-wide one (#48).
329
+ // This pack's system, not the package-wide one.
330
330
  stats: this.stats,
331
331
  });
332
332
  }
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  /**
15
- * **Map notes** — the markdown → Foundry `Scene` translation (issue #1525).
15
+ * **Map notes** — the markdown → Foundry `Scene` translation.
16
16
  *
17
17
  * A map note carries an *essence*: a curated, hand-owned subset of what a Scene
18
18
  * record holds, exactly as a weapon note carries a weapon's essence rather than
@@ -57,7 +57,7 @@ import { compendiumUuid, makeId, MAP_SUBTYPES, MAP_TYPES } from "./ids.mjs";
57
57
  // exactly one the bridge forwards.
58
58
  import { CURATED_REGION_EVENTS, EXCLUDED_REGION_EVENTS } from "./region-events.mjs";
59
59
  // A map's background art is `img`, as every other note type's art is. `image`,
60
- // the spelling a map alone once used, is retired and gone (#149).
60
+ // the spelling a map alone once used, is retired and gone.
61
61
  import { sohlField } from "./frontmatter.mjs";
62
62
 
63
63
  /* -------------------------------------------------------------------- */
@@ -113,7 +113,7 @@ export const MAP_SUBTYPE_PROFILES = Object.freeze({
113
113
  /**
114
114
  * The canvas profile for a map subType.
115
115
  *
116
- * Keyed on the subType rather than the type since #174: every map note is
116
+ * Keyed on the subType rather than the type: every map note is
117
117
  * `type: map`, and which canvas it derives is the one thing the three
118
118
  * spellings ever decided.
119
119
  *
@@ -556,7 +556,7 @@ const SCROLLING_TEXT_EVENTS = Object.freeze([
556
556
  * @type {Readonly<Record<string, {fields: string[], events?: readonly string[]}>>}
557
557
  */
558
558
  const BEHAVIOR_SPECS = Object.freeze({
559
- // The SoHL bridge (#593).
559
+ // The SoHL bridge.
560
560
  trigger: { fields: ["events", "action"], events: CURATED_REGION_EVENTS },
561
561
  adjustDarknessLevel: { fields: ["mode", "modifier"] },
562
562
  applyActiveEffect: { fields: ["effects"] },
@@ -590,7 +590,7 @@ const BEHAVIOR_SPECS = Object.freeze({
590
590
  });
591
591
 
592
592
  /**
593
- * The behaviour types a map note may carry (issue #1525, v1).
593
+ * The behaviour types a map note may carry (v1).
594
594
  *
595
595
  * @type {ReadonlySet<string>}
596
596
  */
@@ -619,7 +619,7 @@ export const BANNED_REGION_BEHAVIOR_TYPES = Object.freeze(
619
619
  [
620
620
  "executeMacro",
621
621
  "its target Macro must ship in the same Adventure to resolve, and " +
622
- "Adventure-bundled macros are not built yet (deferred from #1525).",
622
+ "Adventure-bundled macros are not built yet.",
623
623
  ],
624
624
  ]),
625
625
  );
@@ -13,7 +13,7 @@
13
13
 
14
14
  /**
15
15
  * The published content index — the artifact packages exchange addresses
16
- * through (#239).
16
+ * through.
17
17
  *
18
18
  * **A package publishes its own index; a consumer fetches the ones it depends
19
19
  * on.** That is the whole mechanism, and it replaces a vendored link manifest
@@ -94,7 +94,7 @@ export function metadataFileName(pkg) {
94
94
  * `itemCatalog: true` says a dependency supplies *items*; citing its
95
95
  * *addresses* is a separate edge, and a package may have either without the
96
96
  * other. `harn-ensemble` cites no foreign address and carries 324,016 embedded
97
- * item references (`HeroicLands/harn-ensemble#42`); a package citing addresses
97
+ * item references; a package citing addresses
98
98
  * and needing no items is the mirror of it. Gating the index on the catalogue
99
99
  * flag would serve neither.
100
100
  *
@@ -182,7 +182,7 @@ export function cachedMetadataFiles(config) {
182
182
  * The same fetched indexes, each paired with the package that published it.
183
183
  *
184
184
  * The id is what a SQL content table addresses a dependency's notes by
185
- * (`FROM sohl.notes`, #246), so the pairing has to survive the lookup —
185
+ * (`FROM sohl.notes`), so the pairing has to survive the lookup —
186
186
  * {@link cachedMetadataFiles} drops it, and a caller reconstructing the id from
187
187
  * the file name would be parsing a path to recover something the declaration
188
188
  * already stated.
@@ -230,7 +230,7 @@ export function cachedMetadataIndexes(config) {
230
230
  * rather than being written once per cache. Two copies would be two chances to
231
231
  * get it wrong, and the wrong answer is invisible: every cached version is a
232
232
  * complete, stamped, perfectly valid artifact, so picking the older one reports
233
- * nothing and simply resolves against stale data (#272).
233
+ * nothing and simply resolves against stale data.
234
234
  *
235
235
  * @param {string[]} dirs - Complete cache directories, named `<id>@<version>`.
236
236
  * @returns {string} The newest one.
@@ -426,7 +426,7 @@ export function cachedIndexPath(config, pkg) {
426
426
  * therefore reported against the index, once, instead of once per citing note.
427
427
  *
428
428
  * The realistic cause is a version skew: a dependency released before the
429
- * address grammar gained its `<system>` segment (#59) ships three-segment keys.
429
+ * address grammar gained its `<system>` segment ships three-segment keys.
430
430
  * Re-fetching after that dependency releases is the fix.
431
431
  *
432
432
  * @param {Map<string, object>} foreignIndex - The resolved foreign index.
@@ -13,8 +13,8 @@
13
13
 
14
14
  /**
15
15
  * **Which note types a configuration compiles at all** — the finding for a note
16
- * whose type nothing claims (#146), and the one for a note that loses a
17
- * document while the rest of it compiles (#152).
16
+ * whose type nothing claims, and the one for a note that loses a
17
+ * document while the rest of it compiles.
18
18
  *
19
19
  * Every compile pass answers one question about a note: _is this mine?_ A note
20
20
  * every pass answers "no" to is skipped as quietly as the thousands that
@@ -29,7 +29,7 @@
29
29
  *
30
30
  * This module supplies the missing complaint, and it is asked **once per
31
31
  * build** rather than once per pass. That is not an optimisation: it is the
32
- * only place the question can be answered correctly. #79's rule is that a
32
+ * only place the question can be answered correctly. The rule is that a
33
33
  * markdown type with no mapping in a given system produces no document *for
34
34
  * that system*, silently and correctly — so a per-pass check would report
35
35
  * `armorlocation` against every system that does not map it, which is precisely
@@ -51,13 +51,13 @@
51
51
  * who invented a word. Collapsing the two would send `harn-ensemble` to correct
52
52
  * five perfectly good notes.
53
53
  *
54
- * ## The partial case is a third condition, and it was invisible (#152)
54
+ * ## The partial case is a third condition, and it was invisible
55
55
  *
56
56
  * The table above asks whether a note is compiled *at all*, and a note that
57
57
  * compiles one of its two documents answers yes. But a note produces more than
58
58
  * one document as a matter of course — an item note an Item and the
59
59
  * JournalEntry its prose becomes, a map note a Scene and a JournalEntry, an
60
- * actor note an Actor and a JournalEntry since #337 — so a configuration
60
+ * actor note an Actor and a JournalEntry — so a configuration
61
61
  * missing a pack for *one* of them dropped that document while the rest of the
62
62
  * note compiled into a pack that does exist. The build succeeded and shipped
63
63
  * half of what was written.
@@ -85,7 +85,7 @@
85
85
 
86
86
  import { assertSuppliedCorpus, parseMarkdownFile } from "./helpers.mjs";
87
87
  // The record accessors only: this module is imported by the content index, so
88
- // importing the index back would close a cycle (#243).
88
+ // importing the index back would close a cycle.
89
89
  import { authoredFrontmatter, isNoteRecord, noteFile } from "./index-records.mjs";
90
90
  import { JOURNAL_TYPES, MAP_TYPES, PACK_BY_TYPE, RETIRED_TYPES, currentType } from "./ids.mjs";
91
91
  import { itemTypes } from "./item-registry.mjs";
@@ -124,11 +124,11 @@ export const NEVER_PACKED_TYPES = Object.freeze(new Set([HOMEPAGE_TYPE]));
124
124
  * correctly and is worthless, because the configured vocabulary is *derived
125
125
  * from the routing*. Take a type's route away and it leaves the vocabulary too,
126
126
  * so the inference excuses precisely the mistake it was meant to catch. That is
127
- * not hypothetical: it is #241, where `place`, `lore` and `scenario` were
128
- * declared, validated and unrouted, and every gate reported success until a
129
- * downstream repository failed on 450 notes.
127
+ * not hypothetical: `place`, `lore` and `scenario` were declared, validated
128
+ * and unrouted, and every gate reported success until a downstream repository
129
+ * failed on the notes carrying them.
130
130
  *
131
- * A type leaves this set when it is implemented, the way `bundle` did in #259.
131
+ * A type leaves this set when it is implemented.
132
132
  * The membership is asserted, so it cannot be forgotten in either direction.
133
133
  *
134
134
  * @type {ReadonlySet<string>}
@@ -141,7 +141,7 @@ export const UNIMPLEMENTED_TYPES = Object.freeze(new Set(["vehicle"]));
141
141
  * A folder is the only one, and it is not unclaimed: it compiles to a real
142
142
  * `Folder` document. What it has no answer to is *which* pack claims it, because
143
143
  * that is not a property of the note — a folder materialises in every pack
144
- * holding a document that references it, and its ancestors with it (#257). So
144
+ * holding a document that references it, and its ancestors with it. So
145
145
  * it is exempt from the claim check for the opposite reason a homepage is:
146
146
  * a homepage is in no pack, and a folder may be in several.
147
147
  *
@@ -153,7 +153,7 @@ export const DERIVED_PACKED_TYPES = Object.freeze(new Set([FOLDER_TYPE]));
153
153
  * The note-type → document-subtype maps this toolchain ships.
154
154
  *
155
155
  * Declared in {@link module:engine/subtype-registry} and re-exported here,
156
- * where it has always been read from. It moved to a leaf in #270 so that
156
+ * where it is read from. It sits on a leaf so that
157
157
  * `helpers.mjs` could reach it: this module imports `walkMarkdownTree` from
158
158
  * there, so a dependency the other way would have closed a cycle.
159
159
  *
@@ -232,7 +232,7 @@ function mappingSystems(maps, type) {
232
232
  * @param {ClaimSources} [sources] - What to answer from.
233
233
  * @param {object} [opts] - Options.
234
234
  * @param {readonly object[]} [opts.records] - The corpus, derived once by the
235
- * compile and handed in — required, for the reason above (#243). Defaults to the
235
+ * compile and handed in — required, for the reason above. Defaults to the
236
236
  * configured registries and the systems this toolchain ships.
237
237
  * @returns {ReadonlySet<string>} The note types such a pass would claim.
238
238
  */
@@ -263,8 +263,8 @@ export function noteTypesClaimedBy(docType, sources) {
263
263
  return Object.freeze(new Set(["macro"]));
264
264
  case "Scene":
265
265
  return Object.freeze(new Set(MAP_TYPES));
266
- // The bundles pass: an Adventure is what a `bundle` note compiles into
267
- // (#259). A **prebuilt** Adventure pack still claims nothing —
266
+ // The bundles pass: an Adventure is what a `bundle` note compiles into.
267
+ // A **prebuilt** Adventure pack still claims nothing —
268
268
  // {@link claimedNoteTypes} passes over it, because no note is routed
269
269
  // into a pack whose JSON is checked in rather than compiled.
270
270
  case "Adventure":
@@ -297,13 +297,13 @@ export const CLAIMABLE_DOCUMENT_TYPES = Object.freeze([
297
297
  ]);
298
298
 
299
299
  /**
300
- * Every document class a note of one type compiles into (#152).
300
+ * Every document class a note of one type compiles into.
301
301
  *
302
302
  * **A note produces more than one document, and that is the ordinary case.** An
303
303
  * item note compiles an Item *and* the JournalEntry its prose becomes; a map
304
- * note a Scene and a JournalEntry; since #337 an actor note an Actor and a
304
+ * note a Scene and a JournalEntry; an actor note an Actor and a
305
305
  * JournalEntry too. {@link claimedNoteTypes} unions over the configured packs
306
- * and so answers "is this note compiled *at all*", which is #146's question and
306
+ * and so answers "is this note compiled *at all*", and
307
307
  * cannot see a note that compiles one of its two documents and loses the other.
308
308
  *
309
309
  * Asked of the **claim table** rather than of a list of its own, so the set of
@@ -314,7 +314,7 @@ export const CLAIMABLE_DOCUMENT_TYPES = Object.freeze([
314
314
  * **Union across systems, never per system.** A type one system maps and
315
315
  * another does not appears once, because the `Item` and `Actor` rows already
316
316
  * fold the maps together — so this cannot report a document class a system
317
- * deliberately declines to produce, which is the silence #79 requires.
317
+ * deliberately declines to produce, which is the silence the rule requires.
318
318
  *
319
319
  * ## The JournalEntry row is the one that is per *note*
320
320
  *
@@ -361,7 +361,7 @@ export function documentClassesFor(type, sources, { hasProse } = {}) {
361
361
 
362
362
  /**
363
363
  * Whether a note carries a body at all — the condition `Journals.skipNote`
364
- * applies, asked from the outside (#152).
364
+ * applies, asked from the outside.
365
365
  *
366
366
  * Read from the file rather than from the index record, because a record
367
367
  * carries a note's frontmatter and its derived address and not its prose. The
@@ -388,13 +388,14 @@ function noteHasProse(absPath) {
388
388
  *
389
389
  * The union across the configured pack list, so a type claimed by any one pack
390
390
  * is claimed — which is what keeps a type deliberately unmapped for one system,
391
- * and claimed for another, silent (#79).
391
+ * and claimed for another, silent.
392
392
  *
393
393
  * **A prebuilt pack claims nothing.** Its per-document JSON is checked in
394
394
  * rather than compiled, so it has no pass and no note is routed into one —
395
395
  * which `content-config.mjs` already states by refusing `default: true`
396
396
  * alongside `prebuilt`. Counting it would tell an author their note is claimed
397
- * by a pack that will never look at it. Before #259 the point could not arise:
397
+ * by a pack that will never look at it. With no unimplemented type declared,
398
+ * the point cannot arise:
398
399
  * the only prebuilt pack in the wild is `harn-adventures`'s Adventure pack, and
399
400
  * no compiler was registered for that document type, so the row answered for
400
401
  * nothing whatever it was asked. Now one is.
@@ -404,7 +405,7 @@ function noteHasProse(absPath) {
404
405
  * @param {ClaimSources} [sources] - What to answer from.
405
406
  * @param {object} [opts] - Options.
406
407
  * @param {readonly object[]} [opts.records] - The corpus, derived once by the
407
- * compile and handed in — required, for the reason above (#243).
408
+ * compile and handed in — required, for the reason above.
408
409
  * @returns {ReadonlySet<string>} The claimed note types.
409
410
  */
410
411
  export function claimedNoteTypes(config = loadPackConfig(), sources) {
@@ -428,7 +429,7 @@ export function claimedNoteTypes(config = loadPackConfig(), sources) {
428
429
  * @param {ClaimSources} [sources] - What to answer from.
429
430
  * @param {object} [opts] - Options.
430
431
  * @param {readonly object[]} [opts.records] - The corpus, derived once by the
431
- * compile and handed in — required, for the reason above (#243).
432
+ * compile and handed in — required, for the reason above.
432
433
  * @returns {ReadonlySet<string>} The vocabulary.
433
434
  */
434
435
  export function noteTypeVocabulary(sources) {
@@ -483,7 +484,7 @@ function article(word) {
483
484
  function configurationMessage(type, config, sources) {
484
485
  // Every table below is keyed by the current spelling of a note type; the
485
486
  // message quotes the authored one, which is what the reader has in front of
486
- // them (#78).
487
+ // them.
487
488
  const current = currentType(type);
488
489
  const documents = mappedDocuments(sources.maps, type);
489
490
  if (!documents.length && sources.itemTypes.has(current)) documents.push("Item");
@@ -550,10 +551,10 @@ function specifiedMessage(type) {
550
551
  }
551
552
 
552
553
  /**
553
- * The **partial** finding: the note compiles, and one of its documents does not
554
- * (#152).
554
+ * The **partial** finding: the note compiles, and one of its documents does not.
555
555
  *
556
- * #146's question is "does anything claim this note", and the answer is yes —
556
+ * The unclaimed check asks "does anything claim this note", and the answer is
557
+ * yes —
557
558
  * which is exactly why this went unreported. A note produces more than one
558
559
  * document, and a configuration missing a pack for one of them drops that
559
560
  * document while the rest of the note compiles into a pack that does exist. The
@@ -562,7 +563,8 @@ function specifiedMessage(type) {
562
563
  *
563
564
  * The message names the note, the document class with no pack, and the class
564
565
  * that *did* compile — the last because it is what distinguishes this from
565
- * #146's finding at a glance: the note is not unclaimed, it is half-claimed, and
566
+ * the unclaimed finding at a glance: the note is not unclaimed, it is
567
+ * half-claimed, and
566
568
  * the fix is a pack rather than a `type:`.
567
569
  *
568
570
  * @param {string} type - The note's declared `type`.
@@ -618,7 +620,7 @@ function authoringMessage(type) {
618
620
  * @param {ClaimSources} [sources] - What to answer from.
619
621
  * @param {object} [opts] - Options.
620
622
  * @param {readonly object[]} [opts.records] - The corpus, derived once by the
621
- * compile and handed in — required, for the reason above (#243).
623
+ * compile and handed in — required, for the reason above.
622
624
  * @returns {Array<{file: string, line?: number, column?: number,
623
625
  * severity: "error", message: string, type: string}>} One finding per note.
624
626
  */
@@ -632,7 +634,7 @@ export function unclaimedNoteFindings(config = loadPackConfig(), sources, { reco
632
634
  // and no note is routed into it.
633
635
  const configured = new Set((config.packs ?? []).filter((p) => !p.prebuilt).map((p) => p.type));
634
636
 
635
- // The corpus this compile derived once (#243), required rather than
637
+ // The corpus this compile derived once, required rather than
636
638
  // derived here: this module is imported *by* the content index, so it
637
639
  // could not derive one without closing a cycle — and the caller that wants
638
640
  // this answer is running a compile and already holds it.
@@ -651,13 +653,13 @@ export function unclaimedNoteFindings(config = loadPackConfig(), sources, { reco
651
653
  // A **renamed** spelling is a live type, not an unknown one: it resolves
652
654
  // to the same row, the same registry entry and the same pack. So the
653
655
  // claim is asked of the current spelling while the finding quotes the
654
- // authored one (#78). The rename itself is reported by the frontmatter
656
+ // authored one. The rename itself is reported by the frontmatter
655
657
  // lint, which can say what to write instead.
656
658
  const current = currentType(type);
657
659
 
658
660
  // Every document this note produces, against the classes this
659
661
  // configuration has a pack for. Three outcomes, and the middle one is
660
- // #152's it was invisible while the question was only "is anything
662
+ // the half-claimed one invisible while the question is only "is anything
661
663
  // claiming this note", because the answer there is yes.
662
664
  const produces = documentClassesFor(current, resolved, {
663
665
  // Lazy: only a doc-carrying type whose JournalEntry has nowhere to
@@ -673,7 +675,7 @@ export function unclaimedNoteFindings(config = loadPackConfig(), sources, { reco
673
675
 
674
676
  // Some do and some do not: the note compiles, and one of its documents
675
677
  // is dropped in silence. A type nothing produces at all falls past this
676
- // to the #146 messages below, where `produces` being empty is itself
678
+ // to the unclaimed messages below, where `produces` being empty is itself
677
679
  // part of the answer.
678
680
  if (compiled.length) {
679
681
  findings.push({
@@ -20,7 +20,7 @@
20
20
  * says everything the id said, is readable, and is the identity `content-lint`
21
21
  * already guards: a duplicate address is a build error, while a duplicate `id`
22
22
  * was checked nowhere. So the derived id inherits a guarantee, where the
23
- * authored one had none (#270).
23
+ * authored one had none.
24
24
  *
25
25
  * **An authored `id` always wins.** That is the pattern the map compiler
26
26
  * already uses and documents — `regionDocId(sceneId, key, pinned)` returns
@@ -73,15 +73,15 @@ function text(value) {
73
73
  * the rule but an application of it.** A `Folder` is a document of its own
74
74
  * class, and its id is hashed under the `folder` namespace so that a folder and
75
75
  * an item sharing a shortcode cannot derive one id — a collision Foundry would
76
- * not report, since it keys folders and documents in separate collections
77
- * (#258). So the answer for a folder comes from
76
+ * not report, since it keys folders and documents in separate collections.
77
+ * So the answer for a folder comes from
78
78
  * {@link module:engine/folder-notes.folderDocId}, the pass that emits those
79
79
  * documents, rather than from a second derivation here.
80
80
  *
81
81
  * That this function ever answered differently was invisible from inside a
82
82
  * build — no pass reads a folder's id from here — and surfaced only in the
83
83
  * content index, which is read from outside and had no way to be checked
84
- * against what shipped (#310).
84
+ * against what shipped.
85
85
  *
86
86
  * Returns `undefined` for a file with **no address** — no `type`, or no
87
87
  * `shortcode`. Such a file is not an addressable note, so it has no document
@@ -94,7 +94,7 @@ function text(value) {
94
94
  * @param {object|null|undefined} fm - Parsed frontmatter.
95
95
  * @param {object} [opts]
96
96
  * @param {string} [opts.pkg] - The owning content package. Defaults to the
97
- * configured one, which is the only package a tree's notes belong to (#56).
97
+ * configured one, which is the only package a tree's notes belong to.
98
98
  * @param {readonly object[]} [opts.maps] - The document-subtype maps, which
99
99
  * decide the address's `<system>` segment.
100
100
  * @returns {string|undefined} The document's `_id`, or `undefined` when the
@@ -19,10 +19,10 @@
19
19
  * single-sourced in the repository that ships it — so the package is a property
20
20
  * of the *repository*, not of the note, and no note declares it.
21
21
  *
22
- * It used to be a property of the note, and a **selector**: the compile loop
22
+ * It is not a property of the note, and not a **selector**: as one, the compile loop
23
23
  * read `package:` out of frontmatter and skipped anything that did not match
24
- * the configured value. That is the defect this module exists to remove
25
- * (#56). The skip was silent and it was bucketed as "belongs to another pass",
24
+ * the configured value. That is the defect this module exists to remove.
25
+ * The skip was silent and it was bucketed as "belongs to another pass",
26
26
  * so a tree whose notes named a package no configuration answered to compiled
27
27
  * **zero notes and exited 0** — which is exactly the state the un-migrated
28
28
  * `hm-loc-*` / `hm-adv-*` repositories are in today.
@@ -64,7 +64,7 @@ import { locateFrontmatterKey } from "./retired-fields.mjs";
64
64
  * field, so a collection note that scopes itself with `WHERE … and package =
65
65
  * "sohl"` would match nothing now that the field is gone, and would render an
66
66
  * **empty table** in silence. Supplying the derived value here is what kept the
67
- * sweep mechanical rather than a trap (#56) — and a query that never mentions
67
+ * sweep mechanical rather than a trap — and a query that never mentions
68
68
  * `package` is unaffected either way.
69
69
  *
70
70
  * The frontmatter is copied rather than written into: it is the note's own
@@ -12,12 +12,12 @@
12
12
  */
13
13
 
14
14
  /**
15
- * The shortcodes a note declares it used to be published under (#278).
15
+ * The shortcodes a note declares it used to be published under.
16
16
  *
17
17
  * A package's `(type, shortcode)` addresses are a published interface, and
18
18
  * `addresses diff` reports what a build stopped publishing — telling a
19
19
  * **rename** from a **withdrawal** by matching document ids across two
20
- * releases. #270 removed the property that rested on. An id is now derived from
20
+ * releases. The property that rested on is gone: an id is derived from
21
21
  * the canonical address, which carries the shortcode, so renaming a shortcode
22
22
  * moves the id too: both sides of the join move together, the match finds
23
23
  * nothing, and a rename is reported as a withdrawal with no successor named.
@@ -52,7 +52,7 @@
52
52
  * **It is transient.** Once every baseline a build is compared against post-dates
53
53
  * the rename, the declaration has nothing left to say and may be deleted. That
54
54
  * is the difference from an `id:` pin, which is permanent, opaque, and a second
55
- * identity for a thing that already has one (#270).
55
+ * identity for a thing that already has one.
56
56
  *
57
57
  * **It is one key per note, at the top level, however many systems the note
58
58
  * compiles into.** A shortcode is the note's, not a system block's: a note