@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
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * **Only for a field that declares a `legacyKey`.** Every other field's
10
10
  * in-block position is simply where it lives; reporting those would put a
11
- * finding on every field of every note in every tree, which is #126's corpus
11
+ * finding on every field of every note in every tree, which is the corpus
12
12
  * migration rather than a signal anyone could act on.
13
13
  *
14
14
  * @param {FieldSpec} field - The declaration.
@@ -19,7 +19,7 @@
19
19
  export function readsLegacyKey(field: FieldSpec, from: import("./system-block.mjs").FieldSource): boolean;
20
20
  /**
21
21
  * Whether a resolution read a field from the top-level key `data:` gathered it
22
- * off — the shared level's retiring position (#332).
22
+ * off — the shared level's retiring position.
23
23
  *
24
24
  * {@link readsLegacyKey}'s sibling, and the same signal: a finding here counts
25
25
  * one note still on the pre-`data:` spelling, so the sweep has something to
@@ -42,7 +42,7 @@ export function readsRetiredTopLevel(field: FieldSpec, from: import("./system-bl
42
42
  *
43
43
  * The *position* is resolved by {@link resolveFieldValue} — `<system>.system`
44
44
  * first, then the legacy in-block key, then the declared shared source, then
45
- * the default (#58). The **coercion** is applied here, once, wherever the value
45
+ * the default. The **coercion** is applied here, once, wherever the value
46
46
  * came from: a field's `read` is a statement about the field, not about where
47
47
  * an author happened to write it, so `weight: "7"` reads as `7` at every one of
48
48
  * those positions.
@@ -54,12 +54,12 @@ export function readsRetiredTopLevel(field: FieldSpec, from: import("./system-bl
54
54
  * against. The default is the one block every existing tree authors; a
55
55
  * second system passes its own.
56
56
  * @param {(field: FieldSpec) => void} [options.onLegacyKey] - Called with each
57
- * field read from the position it is being swept off (#305). A callback
57
+ * field read from the position it is being swept off. A callback
58
58
  * rather than a returned list because the caller is a compiler, which already
59
59
  * knows the note and how to locate a key in it; this module knows neither and
60
60
  * would have to invent a finding shape to say so.
61
61
  * @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - Called with
62
- * each field read from the top-level key `data:` gathered it off (#332). The
62
+ * each field read from the top-level key `data:` gathered it off. The
63
63
  * shared level's counterpart to `onLegacyKey`, and a separate callback
64
64
  * because it is a separate position: a note may have moved one of the two and
65
65
  * not the other, and a caller that conflated them would tell its author to
@@ -78,7 +78,7 @@ export function readField(field: FieldSpec, fm: object, options?: {
78
78
  * every caller wants. A builder has one further question — *should the key be
79
79
  * written at all* — and it cannot be answered from the value: `null` from a
80
80
  * note and `null` from a declared default are the same value and opposite
81
- * facts (#329).
81
+ * facts.
82
82
  *
83
83
  * So the position rides back beside the value, resolved **once**. The
84
84
  * alternative is a builder that calls {@link resolveFieldValue} for the source
@@ -111,7 +111,7 @@ export function readFieldEntry(field: FieldSpec, fm: object, { block, onLegacyKe
111
111
  * The question {@link FieldSpec.omitWhenAbsent} turns on, asked of the
112
112
  * *position* rather than of the value — which cannot answer it, since a
113
113
  * declared `default: null` and an authored `null` are indistinguishable once
114
- * the value is in hand (#329).
114
+ * the value is in hand.
115
115
  *
116
116
  * `undefined` counts as absent whatever position reported it, because writing
117
117
  * the key then emits a value `JSON.stringify` drops — the key present in the
@@ -139,10 +139,10 @@ export function isAuthored(from: import("./system-block.mjs").FieldSource, value
139
139
  * systems declare the same shared source and different destinations.
140
140
  * @param {(field: FieldSpec) => void} [options.onLegacyKey] - Passed through to
141
141
  * {@link readField}: called with each field the note authored at the position
142
- * it is being swept off (#305).
142
+ * it is being swept off.
143
143
  * @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - Passed
144
144
  * through to {@link readField}: called with each field the note authored at
145
- * the top-level key `data:` gathered it off (#332).
145
+ * the top-level key `data:` gathered it off.
146
146
  * @returns {(fm: object) => object} A `system`-block builder.
147
147
  */
148
148
  export function buildFromFields(fields: readonly FieldSpec[], { block, onLegacyKey, onRetiredTopLevel }?: {
@@ -166,8 +166,8 @@ export function authoredFields(fields: readonly FieldSpec[]): FieldSpec[];
166
166
  * The fields of a declaration a note may **never** write.
167
167
  *
168
168
  * The complement of {@link authoredFields} in the direction that matters: those
169
- * are the fields an author may write, these are the ones authoring is an error
170
- * (#330). Everything else in a declaration — a constant, a derived value — is
169
+ * are the fields an author may write, these are the ones authoring is an error.
170
+ * Everything else in a declaration — a constant, a derived value — is
171
171
  * simply not authored, which is a statement about the *builder* rather than
172
172
  * about the author, and says nothing about what happens if a note writes the
173
173
  * path anyway.
@@ -179,19 +179,19 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
179
179
  /**
180
180
  * @typedef {object} FieldSpec
181
181
  * @property {string} to - Dotted path in the emitted `system` block — and,
182
- * since #58, the path a note authors the field at directly:
182
+ * the path a note authors the field at directly:
183
183
  * `<system>.system.<to>`.
184
184
  * @property {string} [name] - The **shared, top-level property this field draws
185
185
  * from** when the note authors no value at `<system>.system.<to>`. Dotted for
186
186
  * a path into a shared container (`data.portrait`), which is now the ordinary
187
- * case: `data:` (#128) puts every type-specific fact under one.
187
+ * case: `data:` puts every type-specific fact under one.
188
188
  *
189
- * It used to mean "frontmatter key under `sohl:`", and that reading is the
189
+ * Read as "frontmatter key under `sohl:`", that meaning is the
190
190
  * degenerate case where the shared source and the system destination happen
191
191
  * to share a name. They constantly do not — one shared `data.portrait` feeds
192
192
  * `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
193
- * declared rather than matched by spelling (#58). The in-block position is
194
- * still read, second, until #126 moves the corpus off it — keyed on
193
+ * declared rather than matched by spelling. The in-block position is
194
+ * still read, second, until the corpus moves off it — keyed on
195
195
  * `legacyKey` where the two spellings differ.
196
196
  *
197
197
  * Absent means the value is not authored at all — see `value`.
@@ -199,8 +199,8 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
199
199
  * the system block** — the second position of the resolution order — when
200
200
  * that is not `name`. Absent, the position is keyed on `name`.
201
201
  *
202
- * The two were one property until #305, which held only while a field's
203
- * shared source and its in-block key were the same word. `data:` (#128) ended
202
+ * As one property the two would hold only while a field's
203
+ * shared source and its in-block key were the same word. `data:` ended
204
204
  * that: a shared source is a path into a container, so `data.species` and
205
205
  * `species` name two different places and no single value reached both.
206
206
  * `name: "species"` could not see `data.species`; `name: "data.species"`
@@ -216,7 +216,7 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
216
216
  * @property {string} [topLevelMeans] - **What the note's top-level key of this
217
217
  * name means instead** — declared only where it means something else, and
218
218
  * stating it removes the shared top-level position from this field's
219
- * resolution order (#218).
219
+ * resolution order.
220
220
  *
221
221
  * A field's `name` doubles as its identity and as the shared property it
222
222
  * draws from, which is right wherever the two levels state the same quantity
@@ -243,7 +243,7 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
243
243
  * if the two positions hold unrelated quantities then the *in-block* position
244
244
  * is not the note-level field either, so a check about the note-level field
245
245
  * reads past it. `engine/frontmatter-lint.mjs` resolves that through
246
- * `collidingBlockKeys`. Read for the emitted field alone until #312, which is
246
+ * `collidingBlockKeys`. Reading it for the emitted field alone is
247
247
  * how an affiliation's office style came to answer for its page heading.
248
248
  * @property {string} [shape] - Human-readable shape, for documentation. Comes
249
249
  * paired with `read` from one of the coercion constants below.
@@ -259,10 +259,9 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
259
259
  * third step of {@link module:engine/system-block.resolveFieldValue}'s order,
260
260
  * so the value resolves whichever region carries it. What it tells the
261
261
  * author-facing surfaces is which of the two is the field's *home*, so a
262
- * message about it names `img` rather than sending an author to `sohl.img`
263
- * (#142).
262
+ * message about it names `img` rather than sending an author to `sohl.img`.
264
263
  * @property {"string"|"number"|"boolean"|"list"|"map"} [kind] - The value's
265
- * shape, for the frontmatter linter (#19). Distinct from `shape`, which is
264
+ * shape, for the frontmatter linter. Distinct from `shape`, which is
266
265
  * prose for a reader, and from `read`, which is what the compiler does: a
267
266
  * field may declare `kind` without changing a byte of what it emits, and
268
267
  * several do — `weight` is coerced leniently but is still a number, and
@@ -275,7 +274,7 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
275
274
  * @property {any|((fm: object) => any)} [value] - For a field with no `name`:
276
275
  * the constant, or a function deriving it from the frontmatter.
277
276
  * @property {boolean} [omitWhenAbsent] - **The key is left out entirely when
278
- * the note does not carry the field** (#329), rather than written from a
277
+ * the note does not carry the field**, rather than written from a
279
278
  * declared default.
280
279
  *
281
280
  * Every other field answers absence with a value: an unauthored `weight` is
@@ -310,7 +309,7 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
310
309
  * which the generated reference states in the field's own row.
311
310
  * @property {string} [runtimeOnly] - **What the field holds once play has
312
311
  * started** — declared on a field the *document* writes for itself, which no
313
- * note may author (#330).
312
+ * note may author.
314
313
  *
315
314
  * A schema declares plenty of fields a compiled document has no business
316
315
  * carrying: an affliction's `onsetDate` is the world time its onset fired
@@ -404,7 +403,7 @@ export const BLANK_IS_DEFAULT: Readonly<{
404
403
  export type FieldSpec = {
405
404
  /**
406
405
  * - Dotted path in the emitted `system` block — and,
407
- * since #58, the path a note authors the field at directly:
406
+ * the path a note authors the field at directly:
408
407
  * `<system>.system.<to>`.
409
408
  */
410
409
  to: string;
@@ -412,14 +411,14 @@ export type FieldSpec = {
412
411
  * - The **shared, top-level property this field draws
413
412
  * from** when the note authors no value at `<system>.system.<to>`. Dotted for
414
413
  * a path into a shared container (`data.portrait`), which is now the ordinary
415
- * case: `data:` (#128) puts every type-specific fact under one.
414
+ * case: `data:` puts every type-specific fact under one.
416
415
  *
417
- * It used to mean "frontmatter key under `sohl:`", and that reading is the
416
+ * Read as "frontmatter key under `sohl:`", that meaning is the
418
417
  * degenerate case where the shared source and the system destination happen
419
418
  * to share a name. They constantly do not — one shared `data.portrait` feeds
420
419
  * `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
421
- * declared rather than matched by spelling (#58). The in-block position is
422
- * still read, second, until #126 moves the corpus off it — keyed on
420
+ * declared rather than matched by spelling. The in-block position is
421
+ * still read, second, until the corpus moves off it — keyed on
423
422
  * `legacyKey` where the two spellings differ.
424
423
  *
425
424
  * Absent means the value is not authored at all — see `value`.
@@ -430,8 +429,8 @@ export type FieldSpec = {
430
429
  * the system block** — the second position of the resolution order — when
431
430
  * that is not `name`. Absent, the position is keyed on `name`.
432
431
  *
433
- * The two were one property until #305, which held only while a field's
434
- * shared source and its in-block key were the same word. `data:` (#128) ended
432
+ * As one property the two would hold only while a field's
433
+ * shared source and its in-block key were the same word. `data:` ended
435
434
  * that: a shared source is a path into a container, so `data.species` and
436
435
  * `species` name two different places and no single value reached both.
437
436
  * `name: "species"` could not see `data.species`; `name: "data.species"`
@@ -450,7 +449,7 @@ export type FieldSpec = {
450
449
  * - **What the note's top-level key of this
451
450
  * name means instead** — declared only where it means something else, and
452
451
  * stating it removes the shared top-level position from this field's
453
- * resolution order (#218).
452
+ * resolution order.
454
453
  *
455
454
  * A field's `name` doubles as its identity and as the shared property it
456
455
  * draws from, which is right wherever the two levels state the same quantity
@@ -477,7 +476,7 @@ export type FieldSpec = {
477
476
  * if the two positions hold unrelated quantities then the *in-block* position
478
477
  * is not the note-level field either, so a check about the note-level field
479
478
  * reads past it. `engine/frontmatter-lint.mjs` resolves that through
480
- * `collidingBlockKeys`. Read for the emitted field alone until #312, which is
479
+ * `collidingBlockKeys`. Reading it for the emitted field alone is
481
480
  * how an affiliation's office style came to answer for its page heading.
482
481
  */
483
482
  topLevelMeans?: string | undefined;
@@ -511,13 +510,12 @@ export type FieldSpec = {
511
510
  * third step of {@link module :engine/system-block.resolveFieldValue}'s order,
512
511
  * so the value resolves whichever region carries it. What it tells the
513
512
  * author-facing surfaces is which of the two is the field's *home*, so a
514
- * message about it names `img` rather than sending an author to `sohl.img`
515
- * (#142).
513
+ * message about it names `img` rather than sending an author to `sohl.img`.
516
514
  */
517
515
  shared?: boolean | undefined;
518
516
  /**
519
517
  * - The value's
520
- * shape, for the frontmatter linter (#19). Distinct from `shape`, which is
518
+ * shape, for the frontmatter linter. Distinct from `shape`, which is
521
519
  * prose for a reader, and from `read`, which is what the compiler does: a
522
520
  * field may declare `kind` without changing a byte of what it emits, and
523
521
  * several do — `weight` is coerced leniently but is still a number, and
@@ -539,7 +537,7 @@ export type FieldSpec = {
539
537
  value?: any | ((fm: object) => any);
540
538
  /**
541
539
  * - **The key is left out entirely when
542
- * the note does not carry the field** (#329), rather than written from a
540
+ * the note does not carry the field**, rather than written from a
543
541
  * declared default.
544
542
  *
545
543
  * Every other field answers absence with a value: an unauthored `weight` is
@@ -577,7 +575,7 @@ export type FieldSpec = {
577
575
  /**
578
576
  * - **What the field holds once play has
579
577
  * started** — declared on a field the *document* writes for itself, which no
580
- * note may author (#330).
578
+ * note may author.
581
579
  *
582
580
  * A schema declares plenty of fields a compiled document has no business
583
581
  * carrying: an affliction's `onsetDate` is the world time its onset fired
@@ -27,7 +27,7 @@ export function folderAddress(pkg: string, shortcode: string): string;
27
27
  * never run this build. Deriving it twice is how the index came to publish a
28
28
  * plausible-looking id that resolved to nothing — the general id rule hashes a
29
29
  * note's address under the `document` namespace, and a folder's is hashed under
30
- * {@link FOLDER_ID_NAMESPACE} (#310).
30
+ * {@link FOLDER_ID_NAMESPACE}.
31
31
  *
32
32
  * An **authored `id` still wins**, and is applied by the caller: this is the
33
33
  * derivation, not the resolution, so the pin rule stays stated once, where
@@ -42,8 +42,7 @@ export function folderDocId(pkg: string, shortcode: string): string;
42
42
  * Collect every folder note in a content tree.
43
43
  *
44
44
  * The walk is the caller's to supply, so this stays testable without a tree on
45
- * disk and so one build cannot disagree with another about what the corpus is
46
- * (#243).
45
+ * disk and so one build cannot disagree with another about what the corpus is.
47
46
  *
48
47
  * @param {Iterable<{frontmatter: object|null, absPath: string}>} notes - As
49
48
  * yielded by `walkMarkdownTree`.
@@ -62,7 +61,7 @@ export function collectFolderNotes(notes: Iterable<{
62
61
  *
63
62
  * Three keys per folder, and no more: the canonical address, the
64
63
  * `folder-<shortcode>` short form, and the bare shortcode. They are the
65
- * suffixes of the canonical address the grammar admits (#273) — a `packFolder`
64
+ * suffixes of the canonical address the grammar admits — a `packFolder`
66
65
  * or `parent` field supplies the type itself, so a bare shortcode is a complete
67
66
  * address there.
68
67
  *
@@ -101,7 +100,7 @@ export function folderDocument(folder: FolderNote, parent: FolderNote | null, do
101
100
  * Refuse a note that declares the retired `folder:` spelling.
102
101
  *
103
102
  * `folder:` named a compendium folder by the raw Foundry id declared in a
104
- * per-pack `*-folders.yaml`. Both halves are retired together (#260): the id
103
+ * per-pack `*-folders.yaml`. Both halves are retired together: the id
105
104
  * spelling has nothing left to resolve against once the YAML is gone, and the
106
105
  * YAML has no reader once the spelling is refused.
107
106
  *
@@ -112,7 +112,7 @@ export function fetchCatalogFromPath(config: object, rel: {
112
112
  id: string;
113
113
  }, source: string): Promise<string>;
114
114
  /**
115
- * Fetch one dependency's published content index (#239).
115
+ * Fetch one dependency's published content index.
116
116
  *
117
117
  * **The chain is entirely declared.** The relationship names the dependency's
118
118
  * manifest, the manifest advertises `flags.metadataUrl`, and that URL is the
@@ -178,7 +178,7 @@ export function fetchAllCatalogs(config: object): Promise<number>;
178
178
  * Reads the cache only. A cold cache is an error naming the command that fills
179
179
  * it, rather than a download nobody asked for.
180
180
  *
181
- * **Scoped to one system when the caller compiles for one (#58)**, exactly as
181
+ * **Scoped to one system when the caller compiles for one**, exactly as
182
182
  * {@link module:engine/generate.itemPackJsonDirs} scopes the local half. The
183
183
  * two halves answer the same lookup — `loadItemsMap` merges them into one
184
184
  * address space keyed by `subType:shortcode` — so scoping only the local one
@@ -200,7 +200,7 @@ export function foreignItemCatalogDirs(config: object, system?: string | null):
200
200
  package: string;
201
201
  }>;
202
202
  /**
203
- * The file a system publishes its `system` field sets as (#60).
203
+ * The file a system publishes its `system` field sets as.
204
204
  *
205
205
  * @type {string}
206
206
  */
@@ -20,8 +20,8 @@ export function anchorsOf(entryUuid: string, entryId: string, body: string, name
20
20
  * An item note produces **two**: the item, and separately the JournalEntry its
21
21
  * prose compiles into. They are two documents with two UUIDs, so they get two
22
22
  * addresses; the item's entry points at the other by address rather than
23
- * repeating its UUID, because the doc entry owns that fact (#1499). A `macro`
24
- * note is the same arrangement (#1514), which is why the type set comes from
23
+ * repeating its UUID, because the doc entry owns that fact. A `macro`
24
+ * note is the same arrangement, which is why the type set comes from
25
25
  * {@link hasDocEntry} rather than being spelled here — the journals compiler
26
26
  * reads the same one, so a manifest cannot claim documentation nothing compiled.
27
27
  *
@@ -33,7 +33,7 @@ export function anchorsOf(entryUuid: string, entryId: string, body: string, name
33
33
  * UUID. The two are one fact — a UUID ends in the id — but only the entry knows
34
34
  * which derivation produced it: an item's is its note's `fm.id`, and its
35
35
  * documentation journal's is {@link itemDocEntryId} of that. Stating it here is
36
- * what lets the content index publish an identity it did not re-derive (#310).
36
+ * what lets the content index publish an identity it did not re-derive.
37
37
  *
38
38
  * @param {object} ctx - Resolved identities: `{ contentPackage,
39
39
  * foundryPackageId, packRouter }`.
@@ -44,12 +44,11 @@ export function entriesForNote(fm: object, name: string, address: string, body:
44
44
  * Every note this package publishes, as manifest entries.
45
45
  *
46
46
  * Every note in the tree is this package's, so nothing here selects by package:
47
- * the key's first segment is `contentPackage` (#56). A note still declaring the
47
+ * the key's first segment is `contentPackage`. A note still declaring the
48
48
  * retired `package:` or `draft:` field **throws** rather than being skipped —
49
49
  * skipping one silently is how a whole tree came to be filtered out of a
50
50
  * manifest that then claimed the package published nothing, and it is what let
51
- * a drafted note's inbound links look like links to a note that never existed
52
- * (#69).
51
+ * a drafted note's inbound links look like links to a note that never existed.
53
52
  *
54
53
  * A note that has no address is **reported, not guessed** — the finding carries
55
54
  * the file and the reason, so a caller can print it or fail on it. Inventing an
@@ -93,7 +92,7 @@ export function foundryIdentities(config?: object): {
93
92
  * {@link foundryIdentities} plus what only a *manifest* emission needs. The
94
93
  * split is what lets the content index derive the same Foundry addresses from
95
94
  * the same code without also depending on whether the package publishes pages,
96
- * which is no part of a UUID (#239).
95
+ * which is no part of a UUID.
97
96
  *
98
97
  * @param {object} [config] - A resolved configuration; loaded when omitted.
99
98
  * @returns {{contentPackage: string, foundryPackageId: string, packRouter: object,
@@ -11,13 +11,13 @@
11
11
  * than reported wholesale.
12
12
  */
13
13
  /**
14
- * Every system a configuration says its tree carries (#58).
14
+ * Every system a configuration says its tree carries.
15
15
  *
16
16
  * **Which systems a package ships for is already declared**, in three places
17
17
  * that answer different questions, so this reads all three rather than asking a
18
18
  * new one:
19
19
  *
20
- * - `systems:` (#48) declares them without requiring one, which is how a
20
+ * - `systems:` declares them without requiring one, which is how a
21
21
  * package ships for several;
22
22
  * - a **pack's** `system:` is the same statement made per pack, and it is the
23
23
  * one some trees make: `harn-ensemble` declares an `actors-sohl` and an
@@ -40,7 +40,7 @@
40
40
  export function declaredSystems(config?: object): string[];
41
41
  /**
42
42
  * The system blocks a configuration says its tree carries, and what each
43
- * accepts (#58).
43
+ * accepts.
44
44
  *
45
45
  * The lint checks the blocks its caller names, and for as long as there was one
46
46
  * system the only caller named none — so every tree took the `sohl:` of
@@ -115,7 +115,7 @@ export function matchesKind(value: unknown, kind: string): boolean;
115
115
  * @param {object} [opts.index] - The link index, for the reference check. Its
116
116
  * absence skips that check rather than reporting every reference as dead.
117
117
  * @param {Record<string, object>} [opts.vocabulary] - Type → the closed regions
118
- * it declares, as `engine/note-vocabulary.mjs` states them (#128). Supplied
118
+ * it declares, as `engine/note-vocabulary.mjs` states them. Supplied
119
119
  * by the caller for the same reason `schemas` is: this module validates a
120
120
  * note against whatever its type declares and knows no type names of its
121
121
  * own. Its absence skips the `data:` and `subType` checks rather than
@@ -125,7 +125,7 @@ export function matchesKind(value: unknown, kind: string): boolean;
125
125
  * declaration, asked through `engine/generate.mjs`'s `emittedArtFor`.
126
126
  * Supplied by the caller like `schemas`, so this module states no list of
127
127
  * iconless types of its own; absent it, an inert `img:` goes unreported
128
- * rather than every note's being (#349).
128
+ * rather than every note's being.
129
129
  * @param {Readonly<Record<string, SystemBlockSpec>>} [opts.systems]
130
130
  * The system blocks to check, and what each accepts. Supplied by the caller
131
131
  * for the same reason `schemas` is — a build derives them from its
@@ -155,7 +155,7 @@ export function lintNote(note: object, { schemas, index, vocabulary, packs, emit
155
155
  * @param {object} opts
156
156
  * @param {Record<string, readonly object[]>} opts.schemas - Type → declaration.
157
157
  * @param {Record<string, object>} [opts.vocabulary] - Type → the closed regions
158
- * it declares (#128); see {@link lintNote}.
158
+ * it declares; see {@link lintNote}.
159
159
  * @param {boolean} [opts.references=true] - Whether to check references.
160
160
  * @param {Readonly<Record<string, SystemBlockSpec>>} [opts.systems]
161
161
  * The system blocks to check; see {@link lintNote} and
@@ -191,7 +191,7 @@ export function lintFrontmatter(index: object, { schemas, vocabulary, packs, emi
191
191
  *
192
192
  * - `folder` — the compendium folder the document is filed in.
193
193
  * - `pack` — which compendium of the note's document type receives it, for a
194
- * repository shipping several (#1566).
194
+ * repository shipping several.
195
195
  * - `archetype` — the archetype flag, read for any note by the walk itself.
196
196
  * - `kbcat` — the knowledgebase category a note is grouped under.
197
197
  *
@@ -10,7 +10,7 @@ export function getFrontmatter(fm: any, key: any, defaultValue?: undefined): any
10
10
  */
11
11
  export function sohlField(fm: any, key: any, defaultValue?: undefined): any;
12
12
  /**
13
- * Read a `sohl:` field, seeing the **destination** position as well (#126).
13
+ * Read a `sohl:` field, seeing the **destination** position as well.
14
14
  *
15
15
  * Five declarations resolve their value by re-reading the note rather than by
16
16
  * taking the one {@link module:engine/system-block.resolveFieldValue} handed
@@ -29,8 +29,8 @@ export function sohlField(fm: any, key: any, defaultValue?: undefined): any;
29
29
  *
30
30
  * `legacyKey` is for the one pair whose two positions are spelled differently:
31
31
  * a projectile authors `impact.die` and stores `impactBase.die`, so the reader
32
- * has to be told both. It is the same split `FieldSpec.name`/`legacyKey` makes
33
- * (#305), for the same reason — one name cannot key two positions.
32
+ * has to be told both. It is the same split `FieldSpec.name`/`legacyKey` makes,
33
+ * for the same reason — one name cannot key two positions.
34
34
  *
35
35
  * @param {object} fm - The note's frontmatter.
36
36
  * @param {string} to - The key at the destination, dotted for a nested one.
@@ -46,7 +46,7 @@ export function sohlSystemField(fm: object, to: string, defaultValue?: any, { le
46
46
  /**
47
47
  * Resolve the `charges` block shared by Mystery and Mystical Ability items.
48
48
  *
49
- * Charge usage is carried by the **maximum** alone (#1129): a `null` max means
49
+ * Charge usage is carried by the **maximum** alone: a `null` max means
50
50
  * the item does not use charges at all, `0` means it is counted but uncapped,
51
51
  * and a positive number is a real cap. `value` is the current count, with
52
52
  * `null` meaning "infinite remaining". Both persist as nullable NumberFields,
@@ -87,7 +87,7 @@ export function resolveCharges(fm: object): {
87
87
  export function resolveSkillAptitudes(fm: object, ctx?: string): Record<string, number>;
88
88
  /**
89
89
  * Resolve an affiliation's `relation` map — the shortcode of another
90
- * affiliation → this one's standing toward it (#1404).
90
+ * affiliation → this one's standing toward it.
91
91
  *
92
92
  * An unrecognized standing would fail the schema's `choices` validation at load
93
93
  * and be dropped silently, shipping an affiliation whose authored hostility had
@@ -138,12 +138,12 @@ export function parseValueDesc(raw: any): {
138
138
  * (`folder-poisonsandtoxins`), resolved through the address index the whole
139
139
  * build shares. The `folder:` Foundry-id spelling this function once read
140
140
  * beside it, and the per-pack `*-folders.yaml` that id was resolved against,
141
- * are retired together (#260) — a note declaring `folder:` is refused by
141
+ * are retired together — a note declaring `folder:` is refused by
142
142
  * {@link module:engine/folder-notes.assertNoDeclaredFolder} rather than
143
143
  * reaching here, so there is no second source for a value to come from.
144
144
  *
145
145
  * **`packFolder` was a path for one release** (`Possessions/Misc_Gear/Cooking`)
146
- * and is an address now (#255). A path encoded the hierarchy *in the value*, so
146
+ * and is an address now. A path encoded the hierarchy *in the value*, so
147
147
  * reparenting a folder made every note naming it wrong — a structural edit
148
148
  * became a corpus-wide rewrite. An address is stable under reparenting, which
149
149
  * is exactly why a note is addressed by `(type, shortcode)` and never by
@@ -10,7 +10,7 @@
10
10
  export function compilerFor(docType: string, system?: string | null): Function | undefined;
11
11
  /**
12
12
  * The art fields a note of one content type reaches its document through, and
13
- * the document it reaches (#349).
13
+ * the document it reaches.
14
14
  *
15
15
  * **Derived, never listed.** A note's type routes to a document type
16
16
  * ({@link packForType}), a document type routes to the pass that compiles it
@@ -39,7 +39,7 @@ export function emittedArtFor(type: string): {
39
39
  * The generated JSON of **every** configured Item pack — what the actors pass
40
40
  * reads its predefined items from.
41
41
  *
42
- * All of them, not the first: a repository may ship several Item packs (#1566),
42
+ * All of them, not the first: a repository may ship several Item packs,
43
43
  * and an actor's embedded items may be sourced from any of them. Finding one
44
44
  * pack and stopping is how embedded-item resolution would silently miss every
45
45
  * item that landed in another. Returned in configured order, which is also the
@@ -47,7 +47,7 @@ export function emittedArtFor(type: string): {
47
47
  * order among packs of one type — and every one of them is written before the
48
48
  * actors pass that reads them.
49
49
  *
50
- * **Scoped to one system when the caller has one (#58).** A being addresses an
50
+ * **Scoped to one system when the caller has one.** A being addresses an
51
51
  * item by `(type, shortcode)`, and that address is unique within one system and
52
52
  * not across two: `skill:sword` is an HM3 skill *and* a SoHL skill, with
53
53
  * different data models behind them. The reference itself is unambiguous — it
@@ -65,7 +65,7 @@ export function emittedArtFor(type: string): {
65
65
  * @returns {string[]} Each Item pack's JSON directory. Empty when the
66
66
  * repository ships no items at all, which is a legitimate package: the actors
67
67
  * pass accepts an empty list and reports an item it cannot resolve per
68
- * `(type, shortcode)` instead, naming the being (#49).
68
+ * `(type, shortcode)` instead, naming the being.
69
69
  */
70
70
  export function itemPackJsonDirs(config?: object, system?: string | null): string[];
71
71
  /**
@@ -74,7 +74,7 @@ export function itemPackJsonDirs(config?: object, system?: string | null): strin
74
74
  * An `Adventure` carries **copies**, not references, so a bundle resolves its
75
75
  * `contents` against compiled output rather than against the content tree — the
76
76
  * same arrangement the actors pass has for `itemsSourceDirs`, generalised to
77
- * every document class an Adventure can hold (#259).
77
+ * every document class an Adventure can hold.
78
78
  *
79
79
  * Two kinds of pack are left out, each because it holds nothing a note
80
80
  * addresses. A **prebuilt** pack's JSON is checked in rather than compiled, so
@@ -99,7 +99,7 @@ export function bundleSourceJsonDirs(config?: object, system?: string | null): R
99
99
  /**
100
100
  * The passes to run, ordered so that each one follows the output it reads.
101
101
  *
102
- * **Declaration order is presentation, not compile order (#73).** The same
102
+ * **Declaration order is presentation, not compile order.** The same
103
103
  * `packs:` list is the manifest's `packs` array, which a consumer orders for a
104
104
  * reader browsing compendiums; the actors pass, meanwhile, resolves each
105
105
  * being's embedded items against the item passes' *output*. Making one list
@@ -149,7 +149,7 @@ export function unsatisfiedPassDependencies(running: readonly object[], config:
149
149
  *
150
150
  * A pack ships blank whenever every note in a full tree was rejected — by a
151
151
  * `selects` that claims nothing, or a `pack:` that routes everything elsewhere
152
- * — and the build then exits 0 (#1502). The empty-tree guard in
152
+ * — and the build then exits 0. The empty-tree guard in
153
153
  * {@link generatePacksJson} cannot see that: the tree is full, it is the
154
154
  * *output* that is empty.
155
155
  *