@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
@@ -80,7 +80,7 @@ A tag that classifies the subject is different, because something queries it. A
80
80
  settlement tagged `village` appears in the list of villages and an untagged one
81
81
  does not, so `vilage` does not merely look wrong: it removes the note from an
82
82
  index, silently, and the index still renders. That is the same failure a
83
- misspelled `data:` key used to be, and it gets the same answer — the vocabulary
83
+ misspelled `data:` key is, and it gets the same answer — the vocabulary
84
84
  is declared, so a near miss is a finding that names what you probably meant.
85
85
 
86
86
  | group | applies to | tags |
@@ -146,9 +146,9 @@ the corpus moves, with the block winning. A field says so by naming each: its
146
146
  shared source, and the legacy in-block key it is being swept off. Reading it
147
147
  from the legacy position is _reported_, so the sweep has a progress signal, and
148
148
  the note compiles to the identical document either way — the same read-both,
149
- report-one shape every other retirement in this format uses. Until #305 the two
150
- were one declaration, so a field could name only one of them, and a row this
151
- table stated was reachable only by a note that had already moved.
149
+ report-one shape every other retirement in this format uses. As one
150
+ declaration the two would let a field name only one of them, and a row this
151
+ table states would be reachable only by a note that had already moved.
152
152
 
153
153
  **A field whose spelling means something else at the note level has no shared
154
154
  source.** The fallback assumes the two vocabularies agree about what a name
@@ -166,8 +166,8 @@ them.** A data model declares everything a document stores, and part of that is
166
166
  what _play_ writes: an affliction's `onsetDate` is the world time its onset
167
167
  fired at. Writing `<system>.system.<field>` reaches such a field as directly as
168
168
  any other — the block is a verbatim passthrough, and the field really is in the
169
- schema — so until #330 a note could stamp one, and the compiled pack shipped one
170
- world's play state to every world that installed it.
169
+ schema — so a note left free to stamp one ships that world's play state to
170
+ every world that installs the compiled pack.
171
171
 
172
172
  So a field the document writes for itself is **declared as such**, and the
173
173
  declaration says both halves of the fact: authoring it is an error naming the
@@ -225,7 +225,7 @@ is a gap in the vocabulary rather than something to write into every note: an
225
225
  override that thousands of notes need is a missing subType value.
226
226
 
227
227
  **A type's `subType` values are stated in one shape**, so that they can be read
228
- and compared to the vocabulary that enforces them (#345). Under the type's
228
+ and compared to the vocabulary that enforces them. Under the type's
229
229
  heading, write `**subType**:` on a line of its own, then one bullet per value —
230
230
  `- <value>` or `- <value>: <definition>` — and nothing else between the marker
231
231
  and the list. A type that has no `subType`, or whose values are not enumerated
@@ -295,7 +295,7 @@ at the note's top level and `portrait` moved under `data:`, because a note's
295
295
  token art is a fact about the _note as a published artefact_ while the portrait
296
296
  is a fact about the _subject_.
297
297
 
298
- **A `data:` source is still read at the top level, for now.** `data:` (#128) did
298
+ **A `data:` source is still read at the top level, for now.** `data:` did
299
299
  not invent the facts it holds — it gathered them out of the top level, where
300
300
  `portrait:` sat beside `img:` — so every key it collected has a **pre-`data:`
301
301
  spelling** that is read after the declared one and reported as retiring. Write
@@ -328,7 +328,7 @@ NA on both sides for that type, and its table says so.
328
328
  template priority on an Actor and not on an Item: `hm3/actors.mjs` writes
329
329
  `flags.hm3.templatePriority`, and HM3's Item pass writes no equivalent, so an HM3
330
330
  item compiled from a template note loses the fact that it is one
331
- (`HeroicLands/package-build#283`). The row states the mapping the format makes;
331
+ The row states the mapping the format makes;
332
332
  the gap is in the pass, not in the table.
333
333
 
334
334
  #### An asset path's first segment says which package owns it
@@ -449,7 +449,7 @@ The shared mapping table above names it `data.templatePriority`, targeting
449
449
 
450
450
  > **`archetype` is the retiring spelling.** It is still read, in the `sohl:`
451
451
  > block and at the top level, so a tree sweeps on its own schedule
452
- > (`HeroicLands/package-build#266`) — but the frontmatter linter refuses it, and
452
+ > — but the frontmatter linter refuses it, and
453
453
  > what is compiled and emitted is `templatePriority` on all three sides.
454
454
  >
455
455
  > It is more than a rename, and the collision is **already live** rather than
@@ -481,7 +481,7 @@ real priority** — the one SoHL's own templates ship at — not an absence.
481
481
  it.** SoHL records it in `system`; HM3 keeps it under its own flag scope,
482
482
  `flags.hm3`, and a note that is not a template writes nothing there rather than a
483
483
  `null` nothing reads. Both of HM3's passes write it — an Item's flag was missing
484
- until `HeroicLands/package-build#283`, which made an item note's priority reach
484
+ until an item note's priority reached
485
485
  SoHL and stop at HM3, with nothing said on either side.
486
486
 
487
487
  **How a winner is chosen.** Opening a Create dialog gathers every candidate
@@ -543,7 +543,7 @@ folder pass for a folder, the journals pass for a documentation journal, this
543
543
  rule for everything else. Re-deriving one from the `canonical` key alone reads
544
544
  correctly and is wrong for a folder, and wrong in a way nothing outside the
545
545
  build can detect: the result is a plausible 16-character id that resolves to
546
- nothing (#310).
546
+ nothing.
547
547
 
548
548
  **Why the address and not an authored string.** A note used to declare an
549
549
  opaque 16-character `id` — 6,343 of them across the four content trees — which
@@ -674,7 +674,7 @@ the journals pack having to declare anything. A folder nothing references
674
674
  materialises nowhere.
675
675
 
676
676
  That derivation is what makes a whole class of defect unrepresentable. The
677
- folder used to be declared twice, once per pack, in two files free to disagree:
677
+ folder declared twice, once per pack, sits in two files free to disagree:
678
678
  `sohl-thalorna` was missing 57 of its item folders from its journal folder file
679
679
  and `sohl-kethira-basic` had no journal folder file at all, so both emitted
680
680
  documentation journals into folders their own pack never declared — silently.
@@ -689,7 +689,7 @@ the first.
689
689
 
690
690
  > **`folder:` was a Foundry id**, resolved against a per-pack
691
691
  > `*-folders.yaml` — five files per tree. Both halves are **retired** together
692
- > (#260): the id spelling has nothing left to resolve against once the YAML is
692
+ > : the id spelling has nothing left to resolve against once the YAML is
693
693
  > gone, and the YAML has no reader once the spelling is refused. A note that
694
694
  > still writes `folder:` fails the build, naming `packFolder` and the line to
695
695
  > rewrite, rather than being ignored — a retired field left ignored reads to
@@ -812,11 +812,11 @@ Only a type whose own document carries a system is redirected this way. A
812
812
  documents are core ones already at `none`, so `macro-autoattack` names the Macro
813
813
  and `docmacro-autoattack` its journal — two live addresses.
814
814
 
815
- **An address is lowercase throughout** (#340). Every segment — package,
815
+ **An address is lowercase throughout**. Every segment — package,
816
816
  system, type and shortcode alike — is `^[a-z0-9]+$`, so a capital anywhere in an
817
817
  address is an error naming the lowercase form.
818
818
 
819
- The shortcode was the exception until #340: it was case-sensitive and routinely
819
+ The shortcode is the one that would otherwise be case-sensitive and routinely
820
820
  mixed (`Clb`, `LtShoe`, `HsTunic`) while the address built from it was lowercased
821
821
  wholesale, so the authored name and its address disagreed. Two names differing
822
822
  only in case are two names nobody can tell apart, and they collapsed onto one
@@ -829,9 +829,9 @@ alphanumeric — so the hyphen is purely a separator. There is no longest-match
829
829
  against a roster and no vocabulary check before splitting.
830
830
 
831
831
  **`type` and `subType` are held to that charset, not merely expected to meet
832
- it** (#206). A type is the first segment of every address, so a hyphen in one is
832
+ it**. A type is the first segment of every address, so a hyphen in one is
833
833
  read back as a segment boundary that was never meant as one. A `subType` reaches
834
- no address since #204 retired sections, but it is held to the same rule all the
834
+ no address, sections being retired, but it is held to the same rule all the
835
835
  same: it is a vocabulary term the whole toolchain keys on, one closed set away
836
836
  from being an address again, and a charset that holds for two of the three
837
837
  segments and half of a fourth is a rule nobody can state. Both are checked
@@ -846,7 +846,7 @@ One declared value broke the rule and has been renamed: a `doc`'s `user-guide`
846
846
  is now **`userguide`**. The old spelling was accepted for one transitional
847
847
  release, as a warning naming the replacement, so the 43 `sohl` notes authoring
848
848
  it were not invalidated by the release that renamed them. Every consumer tree
849
- has swept, so the acceptance is gone (#210) and `user-guide` is refused by the
849
+ has swept, so the acceptance is gone and `user-guide` is refused by the
850
850
  charset check — it contains a hyphen, which is the reason that always applied.
851
851
  No retirement-specific code outlived the sweep.
852
852
 
@@ -870,17 +870,17 @@ There is one namespace, and the pipe is required:
870
870
  | `[[WikiLink\|Text]]` | an address | `Text` |
871
871
  | `[[Name]]` | nothing | a finding |
872
872
 
873
- **A link written without a label addresses nothing** (#180), and the correction
873
+ **A link written without a label addresses nothing**, and the correction
874
874
  is always the same: write `[[type-shortcode|Text]]`.
875
875
 
876
- The bare form used to name an **alias** — a note's own display name, or one of
876
+ The bare form does not name an **alias** — a note's own display name, or one of
877
877
  the names it listed in `aliases:` — looked up within the citing note's type. It
878
878
  was measured before it was retired, and the namespace was empty in practice:
879
879
  across 8,305 wikilinks in three content trees, **not one** bare link resolved to
880
880
  a note. What the index behind it did do was fold every note's `name.full` into
881
881
  itself, so two notes of one type could not share a display name — a rules page
882
882
  and a user guide page both called "Gear" were a build failure whose every
883
- available fix moved a published URL (#179).
883
+ available fix moved a published URL.
884
884
 
885
885
  The top-level `aliases:` that fed it is **retired** and refused. The nested
886
886
  `name.aliases:` is **not**: it is reserved for a use that does not exist yet, so
@@ -901,7 +901,7 @@ page it is written on. It is the label that is required, not a target.
901
901
 
902
902
  #### Every address resolves, and every build says so the same way
903
903
 
904
- An address that names no note **fails the build** (#184) — in the link checker,
904
+ An address that names no note **fails the build** — in the link checker,
905
905
  in the pack compilers and in the site build alike.
906
906
 
907
907
  It was a warning in the checker and, in the site build, nothing at all while any
@@ -909,21 +909,21 @@ linkable package had no vendored manifest. The reasoning was that `[[Sunless
909
909
  Vault]]` might be a placeholder for a note somebody meant to write. That was a
910
910
  property of the **bare** form, which is retired, and the intent behind it has a
911
911
  real spelling now: a note tagged `draft` exists, resolves, compiles and
912
- publishes, and a link to it renders visibly marked (#183). So an address landing
912
+ publishes, and a link to it renders visibly marked. So an address landing
913
913
  nowhere is a typo or an omission, and both want fixing.
914
914
 
915
915
  There are six ways a link can fail, and each is one **error** with one message
916
916
  wherever it is met:
917
917
 
918
- | finding | what it means | the fix |
919
- | ---------------- | ----------------------------------------------- | ------------------------------------------------------ |
920
- | `unlabelled` | no `\|`, so the link addresses nothing | write `[[type-shortcode\|Text]]` |
921
- | `not-an-address` | labelled, but the target is not an address | write the address, not the name |
922
- | `not-lowercase` | any segment of the address is capitalised | lowercase it — every segment is lowercase |
923
- | `unknown-type` | qualified, but names no type this build knows | correct the type segment |
924
- | `unresolved` | parses as an address; nothing publishes it | fix the shortcode, or qualify to reach another package |
925
- | `ambiguous` | _unreachable since #336; kept for the manifest_ | — |
926
- | `unknown-anchor` | the address resolves; the `#section` does not | correct the anchor |
918
+ | finding | what it means | the fix |
919
+ | ---------------- | --------------------------------------------- | ------------------------------------------------------ |
920
+ | `unlabelled` | no `\|`, so the link addresses nothing | write `[[type-shortcode\|Text]]` |
921
+ | `not-an-address` | labelled, but the target is not an address | write the address, not the name |
922
+ | `not-lowercase` | any segment of the address is capitalised | lowercase it — every segment is lowercase |
923
+ | `unknown-type` | qualified, but names no type this build knows | correct the type segment |
924
+ | `unresolved` | parses as an address; nothing publishes it | fix the shortcode, or qualify to reach another package |
925
+ | `ambiguous` | _unreachable; kept for the manifest_ | — |
926
+ | `unknown-anchor` | the address resolves; the `#section` does not | correct the anchor |
927
927
 
928
928
  `ambiguous` no longer fires. An omitted segment defaults rather than wildcarding,
929
929
  so a written target expands to one canonical address and a lookup returns one
@@ -1021,10 +1021,10 @@ having nothing worth summarising.
1021
1021
  Every note in **both** groups still produces its JournalEntry and its web page.
1022
1022
  The difference is only whether a system Actor or Item is created as well.
1023
1023
 
1024
- That has been true of actors only since #337. A being used to produce its Actor
1025
- and nothing else, which left it the one system-bearing note with no address at
1026
- `none` — so a prose link naming it had nowhere to land. It now carries a
1027
- documentation journal like every other such note, addressed
1024
+ That includes actors. A being producing its Actor and nothing else would be
1025
+ the one system-bearing note with no address at `none`, leaving a prose link
1026
+ naming it nowhere to land. It carries a documentation journal like every other
1027
+ such note, addressed
1028
1028
  `<package>-none-docbeing-<shortcode>` beside the Actor's
1029
1029
  `<package>-<system>-being-<shortcode>`.
1030
1030
 
@@ -1116,7 +1116,7 @@ still rendered — the finding is the point, not withholding the output.
1116
1116
  ##### In SQL, over the content index
1117
1117
 
1118
1118
  `dataview` is being replaced by **SQL**, queried over the content index, and both
1119
- fences work while the corpus is converted (#246). The query is real SQL, run by
1119
+ fences work while the corpus is converted. The query is real SQL, run by
1120
1120
  DuckDB — not a dialect maintained by this package.
1121
1121
 
1122
1122
  ````markdown
@@ -1148,7 +1148,7 @@ relational operations:
1148
1148
  | `_section` | Emits a headed table per distinct value, in the order the rows arrive. |
1149
1149
 
1150
1150
  `_section` is why one query replaces the forty near-identical blocks a grouped
1151
- table used to need: the authored `ORDER BY` decides the section order too.
1151
+ table would otherwise need: the authored `ORDER BY` decides the section order too.
1152
1152
 
1153
1153
  **Beware `packFolder`.** It is a note's _pack_ folder, not its directory — the
1154
1154
  directory is `file.folder`. (The `folder` field it replaced is retired; a query
@@ -1351,7 +1351,7 @@ sohl:
1351
1351
  durability: 0
1352
1352
  ```
1353
1353
 
1354
- **A top-level `shortcode:` is retired** (#334). It selected a template, while the
1354
+ **A top-level `shortcode:` is retired**. It selected a template, while the
1355
1355
  `system.shortcode` beside it was the compiled item's identity — one word for two
1356
1356
  things — and it could not say which package a template came from, so an address
1357
1357
  resolved into a dependency only because no local pack claimed it and would have
@@ -1391,7 +1391,7 @@ shortcode)` — compendium/world reconciliation, template shadowing, cohort
1391
1391
  membership, effect and expression references. A `name` cannot stand in: it is
1392
1392
  presentation, free to be localized or to diverge.
1393
1393
 
1394
- **Reordering the list moves no id.** The key used to carry the entry's position,
1394
+ **Reordering the list moves no id.** A key carrying the entry's position
1395
1395
  so inserting an item renumbered every id after it and a re-import created new
1396
1396
  documents beside the old ones — while nothing about those documents had changed,
1397
1397
  only their neighbours. The same is now true of a note's journal pages: an
@@ -1612,7 +1612,7 @@ the heading its page is published under; an affiliation's `system.title` is _the
1612
1612
  style of address the office carries_, Ajaw or Warden, which a being holds by
1613
1613
  virtue of its rank. So the top-level key is **not** a shared source for this
1614
1614
  field, and a note that writes one is stating its own heading and nothing else
1615
- (#218). Author the style of address on the membership — the `system.title` of the
1615
+ . Author the style of address on the membership — the `system.title` of the
1616
1616
  entry in a being's `sohl.items` — or, on a catalogue note that genuinely carries
1617
1617
  one, at `sohl.system.title`.
1618
1618
 
@@ -2111,11 +2111,11 @@ depicts what it depicts. A place's maps are therefore derived — every map whos
2111
2111
  The three differ only in the canvas defaults derived for them, which is why they
2112
2112
  are subTypes of one type rather than three types.
2113
2113
 
2114
- > The three were **types** until package-build#174, which is the shape the notes
2115
- > in the wild still carry. Both are read: a note still writing `type: battlemap`
2116
- > is reported and told what to write instead, exactly as a note writing
2117
- > `type: character` is (SoHL#1580). A consumer's `sections` config keys off the
2118
- > type, so it takes one `map` entry where it carried three.
2114
+ > The three are also readable as **types**, which is the shape notes in the
2115
+ > wild still carry: a note writing `type: battlemap` is reported and told what
2116
+ > to write instead, exactly as a note writing `type: character` is. A
2117
+ > consumer's `sections` config keys off the type, so it takes one `map` entry
2118
+ > rather than three.
2119
2119
 
2120
2120
  **NoteLocation** is `[GridLocation, anchor]` where the `anchor` is an anchor identified in the body of the note, and `GridLocation` represents a particular grid location on the document.
2121
2121
 
@@ -2153,24 +2153,13 @@ compilation, and the server-side migration shim is version-gated on
2153
2153
  single Level is synthesised from `img`, `overlay`, `levelName` and
2154
2154
  `backgroundColor`.
2155
2155
 
2156
- > **`img`, at the note's top level, as every other type's artwork is.** A map
2157
- > alone named it `image` and read it out of the `sohl:` block, so one idea had
2158
- > two spellings and this table had to hedge rather than state a rule
2159
- > ([package-build#142](https://github.com/HeroicLands/package-build/issues/142)).
2160
- > Art is not system-specific — a Scene is a core Foundry document, and a second
2161
- > system would want the identical art — so the field sits beside every other
2162
- > note's `img` rather than inside a system block.
2156
+ > **`img`, at the note's top level, as every other type's artwork is.** Art is
2157
+ > not system-specific a Scene is a core Foundry document, and a second system
2158
+ > would want the identical art so the field sits beside every other note's
2159
+ > `img` rather than inside a system block.
2163
2160
  >
2164
- > `image` was **retired in favour of it**, in the three steps `package:` took
2165
- > (#56), and all three have now run
2166
- > ([package-build#149](https://github.com/HeroicLands/package-build/issues/149)).
2167
- > Through the window both spellings were read, `img` won where a note carried
2168
- > both, and a note still writing `image` got a located **warning** rather than a
2169
- > refusal — it compiled to the byte-identical document, so failing a build over
2170
- > it would have redded a tree that had done nothing wrong. The trees were then
2171
- > swept, and the alias dropped. `image` is now simply not a key a map has: in a
2172
- > `sohl:` block it is reported as unknown, and either way the note is refused
2173
- > for the `img` it never declared.
2161
+ > `image` is **not a key a map has**: in a `sohl:` block it is reported as
2162
+ > unknown, and either way the note is refused for the `img` it never declared.
2174
2163
 
2175
2164
  **Two unit conventions, deliberately.** Geometry — walls, doors, lights, tiles,
2176
2165
  sounds, region shapes — is authored in **pixels**, Foundry's native storage,
@@ -2289,8 +2278,8 @@ way (`homepage-root`).
2289
2278
 
2290
2279
  There is no landing page and no section. A `README.md` used to _be_ its
2291
2280
  section's landing, and a `subType: collection` note with a top-level `section:`
2292
- key was a second way to say the same thing. All of it is retired the second
2293
- rule in #202, the first in #204 — because a section appears in **no address**: a
2281
+ key was a second way to say the same thing. All of it is retired, because a
2282
+ section appears in **no address**: a
2294
2283
  page publishes at `/<package>/<type>-<shortcode>/`, which names no directory. A
2295
2284
  section is what Hugo calls a content directory, and the note format does not
2296
2285
  carry one.
@@ -2440,7 +2429,7 @@ an Adventure pack declared first still compiles last.
2440
2429
 
2441
2430
  Foundry's `Folder` — the grouping documents are filed in, and the last document
2442
2431
  this package compiled from bespoke configuration (`*-folders.yaml`, five files
2443
- per tree) rather than from a note. Those files are retired (#260); a pack that
2432
+ per tree) rather than from a note. Those files are retired; a pack that
2444
2433
  still names one is refused.
2445
2434
 
2446
2435
  ```yaml
@@ -2519,4 +2508,4 @@ the namespace is **`folder`**, not the `document` one [every other note hashes
2519
2508
  under](#the-document-id): a folder and an item sharing a shortcode would
2520
2509
  otherwise derive the same id, and Foundry keys the two in separate collections
2521
2510
  so neither would complain. The content index publishes this value, so a consumer
2522
- reads a folder's id rather than recomputing one (#310).
2511
+ reads a folder's id rather than recomputing one.
package/e2e.mjs CHANGED
@@ -19,7 +19,7 @@
19
19
  * of labour here. Standing a licensed Foundry up, seeding a world whose
20
20
  * Gamemaster password is known, waiting for that world to be *active* rather
21
21
  * than merely reachable, tearing it all down again — none of that is one
22
- * repository's problem, and all of it used to live in one. What runs against
22
+ * repository's problem, and none of it lives in one. What runs against
23
23
  * the served world is named in `packageBuild.e2e.suite`, the same way an asset
24
24
  * transform or a manifest-flags module is named: the repository's code, the
25
25
  * toolchain's plumbing.
@@ -685,7 +685,7 @@ export function findExecutable(name, { cwd, env = process.env } = {}) {
685
685
  *
686
686
  * Asked twice per run, and the second asking is the point: an install running
687
687
  * alongside the suite can take the runner out from under it mid-flight, which
688
- * is precisely the failure that reported itself as green (#153).
688
+ * is precisely the failure that reported itself as green.
689
689
  *
690
690
  * @param {object} opts
691
691
  * @param {readonly string[]} opts.command - The program and its arguments.
@@ -766,7 +766,7 @@ export function freshResults({ paths, since, cwd }) {
766
766
  * exit status on its own cannot call a run green, because every way of stopping
767
767
  * a runner before it starts — a corrupt install, a missing browser, a killed
768
768
  * process, the concurrent `npm ci` that surfaced this — produces a run that
769
- * executed nothing, and nothing is not a pass (#153).
769
+ * executed nothing, and nothing is not a pass.
770
770
  *
771
771
  * This can only ever make a verdict worse. A suite that failed keeps its own
772
772
  * status; a suite that passed on no evidence loses the claim. Never the other
@@ -814,7 +814,7 @@ export function suiteVerdict({ status, vanished = [], declared = [], fresh = []
814
814
  * `MODULE_NOT_FOUND` naming nothing relevant.
815
815
  *
816
816
  * The suite is bracketed by checks rather than trusted on its exit status,
817
- * because a run that never started used to report as green (#153):
817
+ * because a run that never started used to report as green:
818
818
  *
819
819
  * - **Before.** Every executable the command needs is resolved, and a missing
820
820
  * one is an error naming it — rather than a container stood up, a world
@@ -13,14 +13,13 @@
13
13
 
14
14
  /**
15
15
  * **The Actor pass, for any system** — the parts of compiling a note into a
16
- * Foundry Actor that belong to the note format rather than to a game system
17
- * (#139).
16
+ * Foundry Actor that belong to the note format rather than to a game system.
18
17
  *
19
18
  * The whole of it lived in `sohl/actors.mjs`, where the system-specific facts
20
- * were already funnelled through one map (`static documentSubtypes`, added by
21
- * #79) and one block constant. A second system needs everything except the
22
- * shape of the `system` block itself, so that everything moved here and each
23
- * half declares what differs:
19
+ * were already funnelled through one map (`static documentSubtypes`) and one
20
+ * block constant. A second system needs everything except the shape of the
21
+ * `system` block itself, so all of it lives here and each half declares what
22
+ * differs:
24
23
  *
25
24
  * | stated by the subclass | what it decides |
26
25
  * | --- | --- |
@@ -35,7 +34,7 @@
35
34
  * - **Reference translation.** A being addresses its embedded items in the
36
35
  * *note* vocabulary and the catalogue is keyed in the *document's*, so
37
36
  * {@link SystemActorCompiler#embeddedSubtype} translates each reference
38
- * forward through this system's map before the lookup (#140).
37
+ * forward through this system's map before the lookup.
39
38
  * - **Embedding.** Merging a note's overlay onto a catalogue entry, deriving a
40
39
  * stable embedded id from the owning actor and the address, and re-keying the
41
40
  * embedded document and its effects for the LevelDB flattening.
@@ -60,15 +59,15 @@ import { BasePackCompiler } from "./base-compiler.mjs";
60
59
  import { contentPackage } from "./content-package.mjs";
61
60
  // Which Foundry Actor subtype a note's `type` compiles into, and which note
62
61
  // types are actors at all. Looked up in the system's declared map, never
63
- // inferred from the type itself (#79).
62
+ // inferred from the type itself.
64
63
  import { mapsNoteType, noteTypesFor, referencedSubtype } from "./document-subtypes.mjs";
65
64
  import { locateFrontmatterKey } from "./retired-fields.mjs";
66
65
  // An `items:` entry's `system:` overlay is merged verbatim, so it reaches the
67
- // document by a path no field declaration sits on — including, until #330, the
66
+ // document by a path no field declaration sits on — including the
68
67
  // fields the document is supposed to write for itself in play.
69
68
  import { itemFields } from "./item-registry.mjs";
70
69
  import { runtimeOnlyIn, runtimeOnlyMessage } from "./runtime-only-fields.mjs";
71
- // A `model:` is an address, read by the same grammar every wikilink is (#336),
70
+ // A `model:` is an address, read by the same grammar every wikilink is,
72
71
  // so an author writes one form and meets one set of messages.
73
72
  import { readQualifier } from "./wikilinks.mjs";
74
73
 
@@ -132,7 +131,7 @@ export function deepMerge(base, overlay) {
132
131
  * vocabulary, so a reference is translated forward through the system's map
133
132
  * before it reaches this function; see
134
133
  * {@link SystemActorCompiler#embeddedSubtype} for why the translation goes that
135
- * way and not the other (#140).
134
+ * way and not the other.
136
135
  *
137
136
  * @param {string} subType - The Foundry Item subtype.
138
137
  * @param {string} shortcode - The item's `system.shortcode`.
@@ -144,7 +143,7 @@ export function itemAddress(subType, shortcode) {
144
143
 
145
144
  /**
146
145
  * The key one predefined item is held under **for the package that publishes
147
- * it** — the address a `model:` naming that package resolves through (#334).
146
+ * it** — the address a `model:` naming that package resolves through.
148
147
  *
149
148
  * The unqualified {@link itemAddress} stays beside it, and the two answer
150
149
  * different questions. A `model` that names no package means *this* one and
@@ -174,11 +173,12 @@ export function packagedItemAddress(pkg, subType, shortcode) {
174
173
  * A shortcode is case-sensitive and routinely mixed — `Clb`, `LtShoe`,
175
174
  * `HsTunic` — while an **address** is not: `readQualifier` normalises what it
176
175
  * reads, and every canonical address is lowercase. So the moment a `model:` is
177
- * read as an address (#334), `weapongear-clb` has to find the document whose
178
- * `system.shortcode` is `Clb`, and an exact match cannot (#346).
176
+ * read as an address, `weapongear-clb` has to find the document whose
177
+ * `system.shortcode` is `Clb`, and an exact match cannot.
179
178
  *
180
179
  * Folding is safe because the fold is already the address: no two items in any
181
- * published tree differ only by the case of their shortcode, and #340 will make
180
+ * published tree differ only by the case of their shortcode, and the rule
181
+ * makes
182
182
  * that impossible rather than merely true.
183
183
  *
184
184
  * **This is not {@link itemAddress}, and must not become it.** That one seeds
@@ -229,13 +229,13 @@ export function embeddedIdentity(item) {
229
229
  * four corpora holds 180 items, at which 64 bits collide with probability
230
230
  * around 10⁻¹⁵.
231
231
  *
232
- * **It takes no index** (#268). Keying on a position meant reordering a being's
232
+ * **It takes no index**. Keying on a position meant reordering a being's
233
233
  * item list renumbered every id after the change, so a re-import created new
234
234
  * documents beside the old ones — while nothing about those documents had
235
235
  * changed, only their neighbours. The identity always exists or must be stated;
236
236
  * see {@link embeddedIdentity}.
237
237
  *
238
- * Keyed by the **document subtype**, so renaming a note type (#78) leaves every
238
+ * Keyed by the **document subtype**, so renaming a note type leaves every
239
239
  * embedded id where it was.
240
240
  *
241
241
  * @param {string} actorId - The owning actor's id.
@@ -277,7 +277,7 @@ export function loadItemsMap(itemsSourceDirs, foreignSourceDirs = []) {
277
277
  const shadowed = [];
278
278
  for (const itemsSourceDir of itemsSourceDirs) {
279
279
  if (!fs.existsSync(itemsSourceDir)) {
280
- // The generator orders the actors pass after every Item pass (#73),
280
+ // The generator orders the actors pass after every Item pass,
281
281
  // so a whole-package build cannot reach this. What can is a run
282
282
  // restricted to this one pack, or a caller constructing the
283
283
  // compiler itself — neither of which reordering a pack list fixes,
@@ -321,13 +321,13 @@ export function loadItemsMap(itemsSourceDirs, foreignSourceDirs = []) {
321
321
  const { _key, ...rest } = doc;
322
322
  map.set(address, rest);
323
323
  // And under this package's own name, so a `model:` that names this
324
- // package explicitly resolves to the same item (#334).
324
+ // package explicitly resolves to the same item.
325
325
  map.set(catalogueKey(doc.type, shortcode, contentPackage()), rest);
326
326
  }
327
327
  }
328
328
  for (const foreignEntry of foreignSourceDirs) {
329
329
  // Each dependency's directory arrives with the package that published
330
- // it (#334), so a foreign template gets its own canonical address
330
+ // it, so a foreign template gets its own canonical address
331
331
  // rather than sharing the local address space.
332
332
  const foreignDir = typeof foreignEntry === "string" ? foreignEntry : foreignEntry.dir;
333
333
  const foreignPackage = typeof foreignEntry === "string" ? null : foreignEntry.package;
@@ -352,7 +352,7 @@ export function loadItemsMap(itemsSourceDirs, foreignSourceDirs = []) {
352
352
  // eslint-disable-next-line no-unused-vars
353
353
  const { _key, ...rest } = doc;
354
354
  // Its own package-qualified address, which a `model:` naming that
355
- // package resolves through and nothing local can shadow (#334).
355
+ // package resolves through and nothing local can shadow.
356
356
  if (foreignPackage) {
357
357
  map.set(catalogueKey(doc.type, shortcode, foreignPackage), rest);
358
358
  }
@@ -411,8 +411,8 @@ export class SystemActorCompiler extends BasePackCompiler {
411
411
  * because {@link SystemActorCompiler#resolveEmbedded} is called per entry
412
412
  * and has no note lifecycle of its own.
413
413
  *
414
- * `frontmatter-lint.mjs` makes the same finding from frontmatter alone
415
- * (#228), and this does not replace it — the lint is a separate command, so
414
+ * `frontmatter-lint.mjs` makes the same finding from frontmatter alone,
415
+ * and this does not replace it — the lint is a separate command, so
416
416
  * without a check here a colliding pair would compile to two documents with
417
417
  * one `_id` and reach the LevelDB packer as an opaque duplicate key.
418
418
  *
@@ -422,12 +422,12 @@ export class SystemActorCompiler extends BasePackCompiler {
422
422
 
423
423
  // An actor's embedded items are resolved against the *output* of the item
424
424
  // passes, so every Item pack compiles before this one. Declared rather than
425
- // left to the order `packs:` happens to list (#73).
425
+ // left to the order `packs:` happens to list.
426
426
  static readsPackOutputOf = Object.freeze(["Item"]);
427
427
 
428
428
  /**
429
429
  * An Actor **is** a system's data, so this pack takes only notes carrying
430
- * this system's block (#58).
430
+ * this system's block.
431
431
  */
432
432
  static requiresSystemBlock = true;
433
433
 
@@ -449,7 +449,7 @@ export class SystemActorCompiler extends BasePackCompiler {
449
449
 
450
450
  /**
451
451
  * Every package a `model:` may name besides this one — the dependencies
452
- * whose item catalogues were supplied (#334).
452
+ * whose item catalogues were supplied.
453
453
  *
454
454
  * @returns {Set<string>} The dependency package ids.
455
455
  */
@@ -465,10 +465,10 @@ export class SystemActorCompiler extends BasePackCompiler {
465
465
  super(options);
466
466
  // Where the items passes wrote their JSON. Stated by the caller rather
467
467
  // than assumed to be this pack's sibling: the packs' locations are
468
- // configuration, and a consumer may put them anywhere (#1508). Every
469
- // Item pack, because a repository may ship more than one (#1566).
468
+ // configuration, and a consumer may put them anywhere. Every
469
+ // Item pack, because a repository may ship more than one.
470
470
  //
471
- // **Optional, and empty is a legitimate package (#49).** This used to
471
+ // **Optional, and empty is a legitimate package.** This used to
472
472
  // throw unless at least one Item pack was declared, which asked a
473
473
  // package to declare the very thing it may exist not to have. An Item
474
474
  // pack is system-bound by construction — Foundry requires `system` on
@@ -560,7 +560,7 @@ export class SystemActorCompiler extends BasePackCompiler {
560
560
  * The Foundry Item subtype an embedded reference's `type` addresses.
561
561
  *
562
562
  * **The reference is in the note vocabulary; the address is in the
563
- * document's** (#140). An actor writes `(type, shortcode)` with the type an
563
+ * document's**. An actor writes `(type, shortcode)` with the type an
564
564
  * author authors, while {@link itemAddress} keys the predefined items by
565
565
  * the subtype each compiled document carries — so exactly one of the two
566
566
  * sides has to translate, and it is this one. The system's map is a
@@ -583,7 +583,7 @@ export class SystemActorCompiler extends BasePackCompiler {
583
583
  * descriptor must carry enough fields to stand alone. The embedded
584
584
  * item's `_id` is regenerated deterministically from
585
585
  * `(actorId, subType, shortcode, indexKey)` so re-exports are stable —
586
- * from the **document subtype**, so that renaming a note type (#78) leaves
586
+ * from the **document subtype**, so that renaming a note type leaves
587
587
  * every embedded id exactly where it was.
588
588
  * Returns null if the descriptor cannot be resolved.
589
589
  *
@@ -594,7 +594,7 @@ export class SystemActorCompiler extends BasePackCompiler {
594
594
  * `null` for a stand-alone entry.
595
595
  * @param {object} [overlay] - The entry's remaining properties.
596
596
  * @param {string} indexKey - Where the reference sits, for a diagnostic.
597
- * It no longer reaches the id (#268) — it names the entry in a message.
597
+ * It no longer reaches the id — it names the entry in a message.
598
598
  * @param {string} ctx - Diagnostic context (the actor's label).
599
599
  * @param {object} [at] - Where to locate a finding.
600
600
  * @param {string} [at.fmKey] - The frontmatter key the reference sits
@@ -606,7 +606,7 @@ export class SystemActorCompiler extends BasePackCompiler {
606
606
  /**
607
607
  * Read an entry's `model:` — the address of the item it is a copy of.
608
608
  *
609
- * The address grammar is the wikilink one (#336), so a `model` is written at
609
+ * The address grammar is the wikilink one, so a `model` is written at
610
610
  * whatever length says what it means: `skill-wpnc` within this package,
611
611
  * `sohl-sohl-skill-wpnc` to reach another. The system segment defaults from
612
612
  * the block the entry sits in — `<system>.items` — which is what makes the
@@ -615,7 +615,7 @@ export class SystemActorCompiler extends BasePackCompiler {
615
615
  *
616
616
  * It replaced a top-level `shortcode:` that meant something different from
617
617
  * the `system.shortcode` beside it and could not say which package a
618
- * template came from (#334).
618
+ * template came from.
619
619
  *
620
620
  * @param {unknown} model - The authored value.
621
621
  * @param {number} index - The entry's position, for the message.
@@ -675,7 +675,7 @@ export class SystemActorCompiler extends BasePackCompiler {
675
675
  this.errorCount++;
676
676
  return null;
677
677
  }
678
- // A `model:` may name the package its template comes from (#334). Where
678
+ // A `model:` may name the package its template comes from. Where
679
679
  // it does, the packaged address is used and nothing local can shadow
680
680
  // it; where it does not, the unqualified one is, and a local definition
681
681
  // still wins over a dependency's as it always has.
@@ -687,7 +687,7 @@ export class SystemActorCompiler extends BasePackCompiler {
687
687
 
688
688
  // The entry's `system:` overlay is merged verbatim, so it reaches the
689
689
  // document without passing a single field declaration — which left it
690
- // the one position a runtime-only field stayed authorable at once #330
690
+ // the one position a runtime-only field would stay authorable at once
691
691
  // closed the item note's own. Asked of the **overlay** rather than of
692
692
  // the merged result: the template it merges onto is a compiled
693
693
  // document, which by then carries none, and a finding has to name what
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  /**
15
- * The one charset every segment of a canonical address is held to (#59).
15
+ * The one charset every segment of a canonical address is held to.
16
16
  *
17
17
  * An address is a hyphen-joined tuple — `package-system-type-shortcode`, so
18
18
  * `sohl-none-doc-gear` — and it is read back by **counting segments**, with a
@@ -43,7 +43,7 @@
43
43
  * digits only.
44
44
  *
45
45
  * Case *was* deliberately unconstrained, on the reasoning that case has no
46
- * bearing on the separator — which is true, and beside the point (#340).
46
+ * bearing on the separator — which is true, and beside the point.
47
47
  *
48
48
  * **Two names that differ only in case are two names nobody can tell apart.** A
49
49
  * shortcode is how a person names a thing when writing a reference —
@@ -55,10 +55,10 @@
55
55
  * `sohl-sohl-weapongear-clb` and its `_id` derived from that. The authored name
56
56
  * and its address disagreed, and everything downstream keys on the address —
57
57
  * which left two notes differing only in case sharing one address, one `_id` and
58
- * one URL, with nothing to report it. It also forced two exceptions elsewhere:
59
- * #336 had to exempt the shortcode from the lowercase rule it pinned on every
60
- * other segment, and #346 had to fold the shortcode's case in the item catalogue
61
- * because an address is lowercased when read.
58
+ * one URL, with nothing to report it. It also forces two exceptions elsewhere:
59
+ * the shortcode has to be exempted from the lowercase rule pinned on every
60
+ * other segment, and its case has to be folded in the item catalogue because
61
+ * an address is lowercased when read.
62
62
  *
63
63
  * One case, one spelling, no exceptions. Every tree already complies but two,
64
64
  * and nothing in any of them collides when folded.