@heroiclands/package-build 20.3.1 → 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 +75 -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 +196 -165
  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 +139 -92
  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
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  /**
15
- * Diffing a package's published item addresses against a released one (#66).
15
+ * Diffing a package's published item addresses against a released one.
16
16
  *
17
17
  * A package's `(type, shortcode)` addresses are a **published interface**.
18
18
  * Every satellite that declares `itemCatalog: true` assembles its beings out of
@@ -48,10 +48,10 @@
48
48
  * mean" from string similarity would be worse than saying nothing, because a
49
49
  * wrong one sends the reader to the wrong fix.
50
50
  *
51
- * **#270 narrowed that match, and a declaration is what makes up the
51
+ * **That match is narrow, and a declaration is what makes up the
52
52
  * difference.** The join rested on the id being independent of the shortcode: a
53
53
  * note authored its `_id`, so the `Tabri` → `Taburi` commit changed the
54
- * shortcode alone and left the id to join the two sides. Since #270 an id is
54
+ * shortcode alone and left the id to join the two sides. An id is
55
55
  * *derived from the canonical address*, which carries the shortcode — so
56
56
  * renaming a shortcode moves the id too, both sides of the join move together,
57
57
  * and the match finds nothing. It stays exact for a note that **pins** an `id`,
@@ -61,7 +61,7 @@
61
61
  * one.
62
62
  *
63
63
  * So an author who has just renamed a shortcode **says so**, with
64
- * `renamedFrom:` on the note that made the change (#278, and see
64
+ * `renamedFrom:` on the note that made the change (and see
65
65
  * `engine/note-renames.mjs`). That is neither a guess nor an identity match but
66
66
  * testimony from the only party that knows, and the diagnostic reports which of
67
67
  * the two it had rather than blending them — a reader can verify a matched id
@@ -74,7 +74,7 @@
74
74
  *
75
75
  * **Severity is decided per case.** A withdrawal is legitimate — content is
76
76
  * allowed to be retired — so it is reported and does not fail a build. A rename
77
- * is equally legitimate as a decision (#1397's charset rule forces some), which
77
+ * is equally legitimate as a decision (the charset rule forces some), which
78
78
  * is why it does not fail one either; what it must not do is happen in silence.
79
79
  * A caller that wants a gate passes `error` and treats any finding as one.
80
80
  *
@@ -91,7 +91,7 @@ import path from "node:path";
91
91
  import { formatDiagnostic, positionInFrontmatter } from "./diagnostics.mjs";
92
92
  import { positionOfLiteral } from "./diagnostics.mjs";
93
93
  import { assertStatedScope } from "./helpers.mjs";
94
- // The corpus, read from the one pass that derives it (#243). Nothing in the
94
+ // The corpus, read from the one pass that derives it. Nothing in the
95
95
  // index's import graph reaches this module — only `bin/` imports it — so this
96
96
  // is a plain static import, as in the link checker.
97
97
  import { indexRecordsFor, isNoteRecord, noteFile } from "./content-index.mjs";
@@ -218,14 +218,15 @@ export function readItemAddresses(dirs) {
218
218
  * The corpus both reads below share, as content-index records.
219
219
  *
220
220
  * **One walk, not two.** `addresses diff` reads the tree twice — once for the
221
- * declarations and once to place its findings — and until #243 those were two
221
+ * declarations and once to place its findings — and those would otherwise be
222
+ * two
222
223
  * independent walks that each parsed every note. They are now one derivation,
223
224
  * shared: the caller derives the records and hands them to both, so the two
224
225
  * halves of a single command cannot disagree about which files the corpus is,
225
226
  * or about the ids in it.
226
227
  *
227
228
  * **The id is why it matters, and not only tidiness.** `noteFilesById` joins
228
- * tree-side ids against ids read out of the *compiled packs*. Since #270 an id
229
+ * tree-side ids against ids read out of the *compiled packs*. An id
229
230
  * is derived from the canonical address, whose first segment is the content
230
231
  * package — and the tree side used to derive it through `resolveNoteId(fm)`
231
232
  * with no package, which falls back to `contentPackage()` and so to whichever
@@ -315,7 +316,7 @@ export function declaredPredecessors(
315
316
  * @param {Map<string, {to: string, file: string}>} [opts.predecessors] - The
316
317
  * declared renames, from {@link declaredPredecessors}. Omitted, the diff
317
318
  * falls back to the id join alone and reports an unpinned rename as a
318
- * withdrawal, which is what it did before #278.
319
+ * withdrawal, which is what it did before.
319
320
  * @returns {Array<object>} One finding per departed address, in address order
320
321
  * so two runs read the same. `kind` is `"renamed"` (with `to`, and `declared`
321
322
  * when it was the note's word rather than an id match) or `"withdrawn"`.
@@ -431,7 +432,7 @@ export function noteFilesById(contentBase, { skipDirectories, config, records, p
431
432
  * A **declared** rename knows its note without any lookup — the declaration is
432
433
  * how it was found — and is reported at the `renamedFrom:` line rather than the
433
434
  * `shortcode:` line, because that is the line the finding is about and the one
434
- * the author deletes once the declaration has done its work (#278).
435
+ * the author deletes once the declaration has done its work.
435
436
  *
436
437
  * @param {object} finding - One finding from {@link diffItemAddresses}.
437
438
  * @param {Map<string, string>} noteFiles - From {@link noteFilesById}.
@@ -480,7 +481,7 @@ export function locateAddressFinding(finding, noteFiles) {
480
481
  * known — and says so, rather than leaving the reader to wonder whether one was
481
482
  * looked for.
482
483
  *
483
- * **A declared rename says it is declared** (#278). The two claims are not
484
+ * **A declared rename says it is declared**. The two claims are not
484
485
  * equally checkable: an id match is a fact in the artefacts, while a
485
486
  * declaration is an author's word, and a reader deciding whether to trust the
486
487
  * successor needs to know which one they have. Saying "the same document" of a
@@ -19,7 +19,7 @@
19
19
  * `{#dossier}` and `{#spoilers}`. *Which field* each lands in is a system's
20
20
  * business — SoHL writes the first to an actor's `appearance`, HM3 to an
21
21
  * actor's and an item's `description` — but *finding* it is not, so the
22
- * extraction lives here where every compiler reaches it (#139).
22
+ * extraction lives here where every compiler reaches it.
23
23
  *
24
24
  * It was a pair of private functions inside the SoHL actors pass, which is
25
25
  * where the convention was first needed and not where it belongs: the anchors
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  /**
15
- * The anchors a note declares on its headings — read in one place (#243).
15
+ * The anchors a note declares on its headings — read in one place.
16
16
  *
17
17
  * **A leaf, deliberately.** This is asked by the link checker, by the content
18
18
  * index, and by the builds that emit a link, and they cannot all import one
@@ -19,7 +19,7 @@
19
19
  * failed are the same in every pass. They were written out
20
20
  * once per pass — three times when this was filed, five by the time it landed —
21
21
  * so a fix to any of them had to be made everywhere, and the passes drifted
22
- * apart in exactly the places nobody was comparing (#1509).
22
+ * apart in exactly the places nobody was comparing.
23
23
  *
24
24
  * A pass now states only what makes it that pass:
25
25
  *
@@ -43,7 +43,7 @@
43
43
  * `selects` answers *which document type* a pass claims, and it is the same
44
44
  * answer for every pack of that type. Which **pack of that type** a claimed
45
45
  * note lands in is a second question, answered by the pack router from the
46
- * note's own `pack:` declaration (#1566) — so a subclass never has to know that
46
+ * note's own `pack:` declaration — so a subclass never has to know that
47
47
  * its type ships in more than one pack.
48
48
  *
49
49
  * **This is the extension point.** The pack list is data
@@ -74,7 +74,7 @@ import {
74
74
  statsForPack,
75
75
  } from "./helpers.mjs";
76
76
  // The record accessors only — see `engine/index-records.mjs` for why they live
77
- // apart from the index that builds them (#243).
77
+ // apart from the index that builds them.
78
78
  import { isNoteRecord, noteFile } from "./index-records.mjs";
79
79
  import { emitDiagnostic } from "./diagnostics.mjs";
80
80
  import { assertNoDeclaredPackage } from "./note-package.mjs";
@@ -98,7 +98,7 @@ import { locateFrontmatterKey } from "./retired-fields.mjs";
98
98
  * note is one this build **refused** — it declares a retired frontmatter field
99
99
  * — and it is an error; a skipped one legitimately belongs to another pass, and
100
100
  * there are thousands of those. Folding the first into the second is what let a
101
- * whole tree be filtered out in silence (#56).
101
+ * whole tree be filtered out in silence.
102
102
  *
103
103
  * @typedef {object} PassStats
104
104
  * @property {number} compiled - Notes that became a document.
@@ -106,7 +106,7 @@ import { locateFrontmatterKey } from "./retired-fields.mjs";
106
106
  * @property {number} skippedOther - Notes this pass does not claim.
107
107
  * @property {number} declined - Notes this pack **refused** — one declaring a
108
108
  * retired frontmatter field, or one routed to a system pack whose system it
109
- * says nothing about (#58). Counted as errors, never as skips.
109
+ * says nothing about. Counted as errors, never as skips.
110
110
  */
111
111
 
112
112
  /**
@@ -148,7 +148,7 @@ export class BasePackCompiler {
148
148
  * its generated tables expanded and its wikilinks converted.
149
149
  *
150
150
  * False for a pass whose output must be exactly what the author typed —
151
- * the macros pass, whose `command` is executable source (#1514). A pass
151
+ * the macros pass, whose `command` is executable source. A pass
152
152
  * that says so also skips building the content-wide link index it would
153
153
  * never read.
154
154
  *
@@ -165,7 +165,7 @@ export class BasePackCompiler {
165
165
  * passes wrote — so an Actor pass must run after every Item pass, and it
166
166
  * says so here.
167
167
  *
168
- * The generator derives the compile order from this (#73), so the order
168
+ * The generator derives the compile order from this, so the order
169
169
  * `packs:` declares is presentation only — it is the manifest's `packs`
170
170
  * array as well, and a consumer orders that for a reader. A pass that
171
171
  * reads another's output states the dependency once, in the class that
@@ -181,7 +181,7 @@ export class BasePackCompiler {
181
181
 
182
182
  /**
183
183
  * Whether this pass's document **is** a system's data, and therefore takes
184
- * only notes that carry that system's block (#58).
184
+ * only notes that carry that system's block.
185
185
  *
186
186
  * A pack may declare a `system:` — `harn-ensemble` ships an `actors-hm3`
187
187
  * and an `actors-sohl` from one tree — and the note-side half of that is
@@ -212,7 +212,7 @@ export class BasePackCompiler {
212
212
  * legal on every note whatever its type, because
213
213
  * `BLOCK_DOCUMENT_PROPERTIES` maps it onto `document.img` — so a note whose
214
214
  * document has no such property authors it, validates, compiles, and loses
215
- * the value with nothing said. That is #349: `Parrot` in `sohl-thalorna`
215
+ * the value with nothing said: `Parrot` in `sohl-thalorna`
216
216
  * had declared `img:` since long before the art rule existed and compiled
217
217
  * `img: null` exactly as a note declaring nothing does. Naming the fields
218
218
  * here is what lets the lint tell an inert key from a live one.
@@ -237,7 +237,7 @@ export class BasePackCompiler {
237
237
  errorCount = 0;
238
238
 
239
239
  /**
240
- * Emitted-`system` findings, one per `documentType|subtype|field` (#155).
240
+ * Emitted-`system` findings, one per `documentType|subtype|field`.
241
241
  *
242
242
  * A key the compiler writes is on **every** document of a subtype, so
243
243
  * reporting it where it is found would print the same sentence 3,126 times
@@ -301,7 +301,7 @@ export class BasePackCompiler {
301
301
  * never descends into. Required: see {@link assertStatedScope}.
302
302
  * @param {(address: string|null) => string|null} [options.folderResolver] -
303
303
  * Resolves a `packFolder` — a folder note's address — to the Foundry
304
- * folder id it materialises as in this pack (#255, #257).
304
+ * folder id it materialises as in this pack.
305
305
  * @param {string} [options.packName] - The pack this pass writes.
306
306
  * @param {string} [options.docType] - The Foundry document type it holds.
307
307
  * @param {{resolve: Function}} [options.router] - The pack router. Omit it
@@ -347,7 +347,7 @@ export class BasePackCompiler {
347
347
  writable: false,
348
348
  });
349
349
  // The walk's scope, stated by whoever built this pass rather than
350
- // resolved from the working directory (#243). Every walk this compiler
350
+ // resolved from the working directory. Every walk this compiler
351
351
  // makes — its own, the table corpus, the link index, the SQL tables —
352
352
  // uses this one answer.
353
353
  Object.defineProperty(this, "skipDirectories", {
@@ -360,14 +360,14 @@ export class BasePackCompiler {
360
360
  this.router = router;
361
361
  this.routingReporter = routingReporter;
362
362
  // The corpus this compile is running over, derived once by
363
- // `generatePacksJson` and shared by every pass (#243). A pass that is
363
+ // `generatePacksJson` and shared by every pass. A pass that is
364
364
  // handed none derives its own in `prepare`, which is what a consumer
365
365
  // constructing one compiler directly does.
366
366
  this.corpus = corpus;
367
367
  }
368
368
 
369
369
  /**
370
- * The `_stats` block every entry this pass emits is stamped with (#48).
370
+ * The `_stats` block every entry this pass emits is stamped with.
371
371
  *
372
372
  * Per pack rather than per package, because a module may ship the same
373
373
  * content for two systems — `harn-ensemble` has an `actors-hm3` pack and an
@@ -410,8 +410,7 @@ export class BasePackCompiler {
410
410
  * routed *here* and carries nothing for this pack's system is an authoring
411
411
  * mistake with a hollow document at the end of it, not a note that belongs
412
412
  * to another pass. Skipping it quietly is how a whole tree compiles to
413
- * documents nobody can use — the failure mode #1502 and #56 are both
414
- * instances of.
413
+ * documents nobody can use.
415
414
  *
416
415
  * @param {object} fm - The note's frontmatter.
417
416
  * @returns {boolean} True when the note may be compiled here; `false` when
@@ -424,7 +423,7 @@ export class BasePackCompiler {
424
423
  eligibleFor(fm) {
425
424
  if (!this.constructor.requiresSystemBlock || !this.packSystem) return true;
426
425
  if (carriesSystemBlock(fm, this.packSystem)) return true;
427
- // Another system's pack of this document type will claim it (#139). A
426
+ // Another system's pack of this document type will claim it. A
428
427
  // note carrying only `hm3:` routes here because this pack is the
429
428
  // *default* of its document type, and defaults are declared per type
430
429
  // rather than per system — but it is not an incomplete note, it is
@@ -446,7 +445,7 @@ export class BasePackCompiler {
446
445
 
447
446
  /**
448
447
  * A refusal only this pass can make, because its subject is the note's
449
- * **type** (#330).
448
+ * **type**.
450
449
  *
451
450
  * The `assertNo*Field` family above it in the walk is type-agnostic by
452
451
  * construction: it runs before `selects`, so that a note declaring a
@@ -473,7 +472,7 @@ export class BasePackCompiler {
473
472
  * Whether this pass claims a note. **Required.**
474
473
  *
475
474
  * Called only for a note this build compiles — every note in the tree
476
- * belongs to the configured content package (#56) — so a subclass decides
475
+ * belongs to the configured content package — so a subclass decides
477
476
  * on `type` alone.
478
477
  *
479
478
  * @param {object} fm - The note's frontmatter.
@@ -524,7 +523,7 @@ export class BasePackCompiler {
524
523
  // The corpus, and the three whole-tree derivations built over it. Every
525
524
  // one of them is a pure function of (tree, scope, router), which do not
526
525
  // vary between the passes of a single compile — so `generatePacksJson`
527
- // derives them once and hands them to each pass (#243).
526
+ // derives them once and hands them to each pass.
528
527
  //
529
528
  // The measurement that motivated it: compiling `sohl` read every note
530
529
  // **20 times**, four per pass — this link index, the table corpus, the
@@ -580,14 +579,14 @@ export class BasePackCompiler {
580
579
  type: fm.type,
581
580
  id: fm.id,
582
581
  // Where this note is, so a link that resolves nowhere is reported
583
- // at a position an author can open rather than by note name (#17).
582
+ // at a position an author can open rather than by note name.
584
583
  file: absPath,
585
584
  bodyLine,
586
585
  bodyColumn,
587
586
  lineMap,
588
587
  // A `[[#slug]]` self-link addresses the source note, which has no
589
588
  // entry in the index — so where its own documents landed has to
590
- // travel with it (#1566).
589
+ // travel with it.
591
590
  pack: this.router?.resolveOrNull(fm, packForType(fm.type).docType),
592
591
  docPack: this.router?.resolveOrNull(fm, "JournalEntry"),
593
592
  index: this.linkIndex,
@@ -636,8 +635,7 @@ export class BasePackCompiler {
636
635
  }
637
636
 
638
637
  /**
639
- * Report every `<system>.system` key the receiving subtype does not declare
640
- * (#58).
638
+ * Report every `<system>.system` key the receiving subtype does not declare.
641
639
  *
642
640
  * An **error**, not a warning: Foundry drops an unknown `system` key at
643
641
  * construction without a word, so the alternative is a document shipped
@@ -657,8 +655,8 @@ export class BasePackCompiler {
657
655
  */
658
656
  reportUndeclaredSystemData(fm, block, documentType, subType) {
659
657
  const absPath = this.currentNote?.absPath;
660
- // Whose schema, where a build has more than one system: this pack's
661
- // (#139). `undefined` — a pack that declares no system — keeps the
658
+ // Whose schema, where a build has more than one system: this pack's.
659
+ // `undefined` — a pack that declares no system — keeps the
662
660
  // package-wide answer this always used.
663
661
  const findings = checkAuthoredSystemData(fm, {
664
662
  block,
@@ -676,7 +674,7 @@ export class BasePackCompiler {
676
674
 
677
675
  /**
678
676
  * Record every `system` key the *compiled document* carries that the
679
- * receiving subtype does not declare (#155).
677
+ * receiving subtype does not declare.
680
678
  *
681
679
  * The sibling of {@link BasePackCompiler#reportUndeclaredSystemData}, and
682
680
  * the half that sees what no declaration states. A compiler writes keys of
@@ -734,7 +732,7 @@ export class BasePackCompiler {
734
732
  /**
735
733
  * Emit the collected emitted-`system` findings, once each.
736
734
  *
737
- * An **error**, for the reason #60 made its sibling one: the value is gone
735
+ * An **error**, for the reason its sibling is one: the value is gone
738
736
  * at load and the build says nothing, and severity that varied by *which
739
737
  * part of the build wrote the key* would make the less fixable half the
740
738
  * quieter one. What varies is the message, which says whose fix it is —
@@ -859,7 +857,7 @@ export class BasePackCompiler {
859
857
  }
860
858
  if (stats.declined) {
861
859
  // Its own line, at error level: these are not skips, and burying
862
- // them in the skipped tally is the defect (#56). Each one has
860
+ // them in the skipped tally is the defect. Each one has
863
861
  // already been named individually as a diagnostic.
864
862
  log.error(
865
863
  `Declined ${stats.declined} note(s) — each named above, with ` +
@@ -892,7 +890,7 @@ export class BasePackCompiler {
892
890
  // refusal, because it *is* the same refusal: the index defers to
893
891
  // `assertNoDeclaredPackage` for a retired `package:`, which is the
894
892
  // check this loop makes a few lines further down. All that changed is
895
- // which pass sees the note first (#243).
893
+ // which pass sees the note first.
896
894
  if (this.reportsCorpusProblems) {
897
895
  for (const problem of this.corpus.problems ?? []) {
898
896
  stats.declined++;
@@ -911,7 +909,7 @@ export class BasePackCompiler {
911
909
  }
912
910
 
913
911
  // The corpus, from the index this compile derived once — not a walk of
914
- // this pass's own (#243). Each note is then read for its **prose**: the
912
+ // this pass's own. Each note is then read for its **prose**: the
915
913
  // index carries what is *about* a note and deliberately not its text,
916
914
  // nor the `bodyLine`/`bodyColumn` a diagnostic needs, and this pass has
917
915
  // to have the body anyway. So the read is one this pass was already
@@ -922,7 +920,7 @@ export class BasePackCompiler {
922
920
  const absPath = noteFile(this.contentBase, record);
923
921
  const { frontmatter: fm, body, bodyLine, bodyColumn } = parseMarkdownFile(absPath);
924
922
  // Which note this pass is on, so anything it calls can report a
925
- // position without every method having to be handed one (#17).
923
+ // position without every method having to be handed one.
926
924
  this.currentNote = { absPath, bodyLine, bodyColumn };
927
925
  // A file carrying no frontmatter at all is not a note.
928
926
  if (!fm) {
@@ -933,12 +931,12 @@ export class BasePackCompiler {
933
931
  // note is answered whichever pass would have claimed it — and
934
932
  // whatever the declared value says.
935
933
  //
936
- // - `package:` (#56): a note's package is the repository's
934
+ // - `package:`: a note's package is the repository's
937
935
  // configured one, so declaring it restates a constant.
938
- // - `draft:` (#69): it excluded the note from the packs, the
936
+ // - `draft:`: it excluded the note from the packs, the
939
937
  // manifest and the site, and no checker reported the links that
940
938
  // left dangling.
941
- // - `aliases:` (#180): it fed the alias index, which the bare
939
+ // - `aliases:`: it fed the alias index, which the bare
942
940
  // `[[Alias]]` form was looked up in; the form is retired, so the
943
941
  // list has no reader left. The nested `name.aliases` is a
944
942
  // different field and is **not** refused — it is reserved, and
@@ -973,7 +971,7 @@ export class BasePackCompiler {
973
971
  }
974
972
  // The id this note's document is filed under: its authored `id`
975
973
  // if it pins one, otherwise the id derived from its canonical
976
- // address (#270). Resolved for every note this pass claims, and
974
+ // address. Resolved for every note this pass claims, and
977
975
  // through the one function every other corpus reader calls — the
978
976
  // wikilink index, the content index and the Foundry-address pass
979
977
  // must all compute the id this pass compiles under, and none of
@@ -1021,7 +1019,7 @@ export class BasePackCompiler {
1021
1019
  stats.skippedOther++;
1022
1020
  continue;
1023
1021
  }
1024
- // The type-specific half of the retired-field family (#330):
1022
+ // The type-specific half of the retired-field family:
1025
1023
  // what a note of *this* type may not write, which needs the
1026
1024
  // type's own field declaration and so cannot be asked before
1027
1025
  // `selects`. Counted as a declined note for the same reason
@@ -1048,7 +1046,7 @@ export class BasePackCompiler {
1048
1046
  this.errorCount++;
1049
1047
  // `position` is set by whatever failed if it knew where — an
1050
1048
  // unresolved address, a bad table directive — so the report
1051
- // points at the line rather than at the note (#17).
1049
+ // points at the line rather than at the note.
1052
1050
  this.noteError(
1053
1051
  `${this.noteLabel(fm)} failed to compile: ${err.message}`,
1054
1052
  err.position,
@@ -21,9 +21,9 @@
21
21
  * world's collection already holds that `_id`, then creates or updates each.
22
22
  * Afterwards the documents live independently and the Adventure has no further
23
23
  * role. **It is an installer** — a set of document *copies* packaged for
24
- * one-shot import (#259).
24
+ * one-shot import.
25
25
  *
26
- * That is the whole difference from a folder (#256), which is a live grouping
26
+ * That is the whole difference from a folder, which is a live grouping
27
27
  * **by reference** that persists in the pack. A bundle carries copies and
28
28
  * exists to be imported once, so the two are not variations on one idea.
29
29
  *
@@ -140,7 +140,7 @@ export function bareAddress(value) {
140
140
  * `### type: bundle` table puts it — the closed container, so a misspelled
141
141
  * `content` is a finding rather than a silently empty Adventure. It is accepted
142
142
  * at the top level too, exactly as a folder note's `parent` is: an author
143
- * following #259's own example rather than the specification should get a
143
+ * following the shipped example rather than the specification should get a
144
144
  * bundle, not a silent default.
145
145
  *
146
146
  * **Order is the author's**, and it is kept: an Adventure's `SetField` attaches
@@ -269,7 +269,7 @@ export function buildAdventure({
269
269
  _id: id,
270
270
  _stats: stats,
271
271
  // The Foundry *collection* name, not the pack's: a pack directory may
272
- // be called anything (#1566), and the key names the collection the
272
+ // be called anything, and the key names the collection the
273
273
  // record belongs to inside it.
274
274
  _key: `!adventures!${id}`,
275
275
  };
@@ -13,11 +13,11 @@
13
13
 
14
14
  /**
15
15
  * Adventure pack compiler — `type: bundle` notes → Foundry `Adventure`
16
- * documents (#259).
16
+ * documents.
17
17
  *
18
- * The specification and the vocabulary landed in #263, which left the type
18
+ * The specification and the vocabulary leave the type
19
19
  * declared and uncompiled: authoring one said so, in as many words. This is the
20
- * pass, and the two decisions #263 recorded are settled here.
20
+ * pass, and the two decisions it records are settled here.
21
21
  *
22
22
  * **Which pack.** Not the `adventures` **companion**. The scenes pass already
23
23
  * writes one Adventure per pinned place into that pack, and a companion is
@@ -32,7 +32,7 @@
32
32
  * the router's rule for `pack:`, so there is one answer and not two. One note
33
33
  * can compile into two documents — an item and the JournalEntry its prose
34
34
  * became — and the second is bundled only when the note names it by its own
35
- * `doc…` address, which is the address that already exists for it (#1362).
35
+ * `doc…` address, which is the address that already exists for it.
36
36
  * Nothing is inferred: naming `miscgear-bowlcer` puts the *item* in the bundle
37
37
  * and not its description page.
38
38
  *
@@ -41,7 +41,7 @@
41
41
  * a reader of every other one, which it states in
42
42
  * {@link Bundles.readsPackOutputOf} rather than leaving to the order a
43
43
  * consumer happened to write its pack list in — the generator derives the
44
- * compile order from that declaration (#73).
44
+ * compile order from that declaration.
45
45
  *
46
46
  * **A pack's `system:` constrains what its Adventures may hold.** An
47
47
  * `Adventure` has no `system` field, so a bundle spanning two systems cannot be
@@ -83,7 +83,7 @@ import { readQualifier } from "./wikilinks.mjs";
83
83
  * the first.
84
84
  *
85
85
  * A **folder** document is skipped. It is emitted into every pack that holds
86
- * something filed in it (#257), so it is not one pack's document and has no
86
+ * something filed in it, so it is not one pack's document and has no
87
87
  * single note behind it; a bundle that wants folders is a question this pass
88
88
  * refuses rather than guesses at — see {@link Bundles#resolveAddress}.
89
89
  *
@@ -126,7 +126,7 @@ export class Bundles extends BasePackCompiler {
126
126
  /**
127
127
  * Every document type a bundle can hold a copy of.
128
128
  *
129
- * The declaration the generator orders passes by (#73): an Adventure holds
129
+ * The declaration the generator orders passes by: an Adventure holds
130
130
  * *compiled* documents, so every pass that produces one runs first. Stated
131
131
  * here, in the class that does the reading, rather than in each consuming
132
132
  * repository's pack list.
@@ -225,7 +225,7 @@ export class Bundles extends BasePackCompiler {
225
225
  throw new Error(
226
226
  `bundle "${bundleName}" lists the folder "${address}". A ` +
227
227
  `folder is not a member: it materialises in every pack ` +
228
- `holding something filed in it (#257), so it belongs to no ` +
228
+ `holding something filed in it, so it belongs to no ` +
229
229
  `one pack and there is no single copy to bundle. List the ` +
230
230
  `documents instead`,
231
231
  );
@@ -18,7 +18,7 @@
18
18
  * written. Every build-time rewriter that pattern-matches a body therefore has
19
19
  * to know where code is — otherwise a source listing that happens to contain
20
20
  * the rewriter's syntax is silently corrupted. Wikilink conversion met this as
21
- * `[[0]]` inside a fence being turned into a link (#1505), and it depended on
21
+ * `[[0]]` inside a fence being turned into a link, and it depended on
22
22
  * the surrounding literal's shape (`[[1,2],[3,4]]` survived), so the corruption
23
23
  * looked arbitrary.
24
24
  *
@@ -378,12 +378,12 @@ export function matchAllOutsideCode(markdown, pattern, options) {
378
378
  * **Which runs count as code is {@link codeRegions}' rule, not a second copy of
379
379
  * it.** The knowledgebase build carried its own regex once, and it was weaker in
380
380
  * two ways that both corrupted the one page whose subject *is* link syntax — so
381
- * its examples were exactly the input a looser rule mangles (SoHL#1665). A
381
+ * its examples are exactly the input a looser rule mangles. A
382
382
  * single-backtick span was allowed to cross newlines, so one odd backtick paired
383
383
  * with another paragraphs away and every span after it paired wrongly: prose was
384
384
  * masked as code while real spans were left exposed. And only three-backtick
385
385
  * fences were recognised, so a four-backtick example holding a three-backtick
386
- * block — the documented "fences of any length" case (#1505) — leaked its
386
+ * block — the documented "fences of any length" case — leaked its
387
387
  * contents.
388
388
  *
389
389
  * @param {string} body - The markdown body.
@@ -25,13 +25,13 @@
25
25
  *
26
26
  * **This module has no import-time side effects.** It creates no directories,
27
27
  * reads no manifest, configures no logger, and parses no argv — every path and
28
- * pack list is a parameter, defaulted from the resolved build configuration
29
- * (#1508), which a caller may replace wholesale to compile another package's
28
+ * pack list is a parameter, defaulted from the resolved build configuration,
29
+ * which a caller may replace wholesale to compile another package's
30
30
  * tree. Those side effects belong to the command
31
31
  * line that drives it (`bin/build-compendiums.mjs`), so the library can be
32
32
  * imported by another repository's build, or by a test, without a stray
33
33
  * `build/` tree appearing or the shared `loglevel` singleton being
34
- * reconfigured (#1507). In particular, a *module* repository ships
34
+ * reconfigured. In particular, a *module* repository ships
35
35
  * `module.json` rather than `system.template.json`, so importing must not
36
36
  * depend on the latter existing.
37
37
  *
@@ -64,7 +64,7 @@ import { loadPackConfig } from "./pack-config.mjs";
64
64
  * @param {object} opts
65
65
  * @param {object} [opts.config] The resolved build configuration, which
66
66
  * the two path arguments below default from. Supplying one is how a caller
67
- * compiles a package other than this repository's (#1508).
67
+ * compiles a package other than this repository's.
68
68
  * @param {string[]} [opts.sourcePacks] Every pack compiled from the content
69
69
  * tree, in compile order. Defaults to the configured pack directories.
70
70
  * @param {string} [opts.stageDest] Directory the LevelDB packs are built
@@ -73,10 +73,10 @@ import { loadPackConfig } from "./pack-config.mjs";
73
73
  * @throws {Error} If pack JSON generation reported any error. Packs compiled
74
74
  * from incomplete or empty JSON ship blank or short compendiums, and the
75
75
  * omission is invisible until a player looks for content that is not there
76
- * (#1502) — so this is fatal, not a warning, and the caller is expected to
76
+ * — so this is fatal, not a warning, and the caller is expected to
77
77
  * turn it into a failing exit code.
78
78
  * @throws {Error} If a compiled pack ships a Scene that has lost its embedded
79
- * Level (#1538). Fatal for the same reason: Foundry reads a missing Level
79
+ * Level. Fatal for the same reason: Foundry reads a missing Level
80
80
  * record as "no levels" and persists that on the next world launch, so the
81
81
  * map image is gone before anyone notices it was ever at risk. See
82
82
  * {@link verifyPackSceneLevels}.
@@ -137,7 +137,7 @@ export async function compilePacks({
137
137
  // LevelDB key. Nothing in Foundry ties the two together on read: a
138
138
  // missing Level record only warns, and the next world launch persists
139
139
  // the emptied `levels` array — so the map is lost for good and the
140
- // only symptom is a blank battlemap (#1538). Assert it on the bytes
140
+ // only symptom is a blank battlemap. Assert it on the bytes
141
141
  // just written, which is the one place the compendium CLI's write path
142
142
  // is observable.
143
143
  const problems = await verifyPackSceneLevels(stage);
@@ -164,7 +164,7 @@ export async function compilePacks({
164
164
  * @param {number} [options.ownership=0] Value to reset default ownership to.
165
165
  * @param {string} [options.lastModifiedBy] The stamped author id. Defaults to
166
166
  * the configured one — the same value `buildStats` stamps, so a compiled
167
- * entry and a re-cleaned one never disagree (#1508).
167
+ * entry and a re-cleaned one never disagree.
168
168
  */
169
169
  function cleanPackEntry(
170
170
  data,
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  /**
15
- * The corpus one compile runs over, and everything derived from it (#243).
15
+ * The corpus one compile runs over, and everything derived from it.
16
16
  *
17
17
  * **A compile read every note twenty times.** Measured over `sohl`'s 1,685
18
18
  * notes: 33,700 reads, exactly twenty per note. Four per pass — the
@@ -24,10 +24,9 @@
24
24
  * to all of them.
25
25
  *
26
26
  * So they are derived **once**, here, and every pass is handed the result. That
27
- * is #243's claim stated at the point where it costs the most: not that
27
+ * is the claim stated at the point where it costs the most: not that
28
28
  * re-deriving is wasteful, but that N passes each answering "which files are
29
- * the corpus?" is N answers that can differ — which is what #241 was, and what
30
- * the twenty reads were paying for.
29
+ * the corpus?" is N answers that can differ.
31
30
  *
32
31
  * **This module exists apart from the compilers for an import reason.**
33
32
  * Deriving the index reaches the pack router and the manifest emitter, and
@@ -50,7 +49,7 @@ import { prepareTreeSqlTables } from "./sql-tables.mjs";
50
49
  * @param {object} opts - Options.
51
50
  * @param {string} opts.contentBase - Root of the content tree.
52
51
  * @param {readonly string[]} opts.skipDirectories - The scope, stated by the
53
- * caller as every corpus read requires (#243).
52
+ * caller as every corpus read requires.
54
53
  * @param {object} opts.router - The pack router this compile resolved. Shared
55
54
  * by every pass, which is what makes one link index correct for all of them.
56
55
  * @param {object} [opts.config] - The resolved configuration.