@heroiclands/package-build 25.5.5 → 25.6.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 (66) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/assets/styles/content-base.css +1 -1
  3. package/docs/api.md +24 -16
  4. package/docs/authoring/frontmatter.md +1 -1
  5. package/docs/authoring/links-and-markup.md +10 -9
  6. package/docs/content-format.md +40 -1
  7. package/docs/diagnostics.md +2 -2
  8. package/docs/guides/reference-pages.md +4 -1
  9. package/docs/guides/site.md +1 -1
  10. package/docs/reference/format-details.md +174 -129
  11. package/docs/reference/note-types.md +134 -121
  12. package/docs/reference/theme-pages.md +1 -0
  13. package/engine/content-format.yaml +9 -7
  14. package/engine/content-html.mjs +3 -2
  15. package/engine/content-index.mjs +23 -0
  16. package/engine/content-links.mjs +8 -0
  17. package/engine/content-render.mjs +2 -2
  18. package/engine/content-tables.mjs +12 -5
  19. package/engine/culture-reach.mjs +3 -0
  20. package/engine/declared-sections.mjs +100 -0
  21. package/engine/derived-sections.mjs +36 -2
  22. package/engine/dictionary-rows.mjs +2 -1
  23. package/engine/generated-sections.mjs +2 -0
  24. package/engine/holdings.mjs +107 -3
  25. package/engine/index-records.mjs +3 -1
  26. package/engine/index.mjs +1 -1
  27. package/engine/language-data.mjs +0 -1
  28. package/engine/language-rows.mjs +1 -3
  29. package/engine/language-terms.mjs +7 -10
  30. package/engine/map-dot.mjs +22 -0
  31. package/engine/markdown-blocks.mjs +23 -8
  32. package/engine/metadata-index.mjs +1 -0
  33. package/engine/note-addresses.mjs +16 -0
  34. package/engine/note-vocabulary.mjs +19 -8
  35. package/engine/office-holders.mjs +76 -0
  36. package/engine/page-breaks.mjs +36 -8
  37. package/engine/pdf-render.mjs +4 -3
  38. package/engine/place-kinds.mjs +106 -0
  39. package/engine/settlement-tables.mjs +410 -675
  40. package/engine/settlement-terms.mjs +118 -0
  41. package/engine/site-build.mjs +2 -4
  42. package/engine/sql-tables.mjs +101 -7
  43. package/engine/standing-terms.mjs +9 -5
  44. package/engine/standings.mjs +83 -31
  45. package/engine/unique-items.mjs +85 -0
  46. package/package.json +9 -4
  47. package/site-theme/data/banner-kinds.yaml +2 -0
  48. package/sohl/actors.mjs +4 -3
  49. package/types/engine/content-html.d.mts +2 -1
  50. package/types/engine/culture-reach.d.mts +3 -0
  51. package/types/engine/declared-sections.d.mts +30 -0
  52. package/types/engine/derived-sections.d.mts +5 -1
  53. package/types/engine/holdings.d.mts +34 -2
  54. package/types/engine/index-records.d.mts +2 -1
  55. package/types/engine/language-terms.d.mts +5 -1
  56. package/types/engine/markdown-blocks.d.mts +5 -1
  57. package/types/engine/office-holders.d.mts +33 -0
  58. package/types/engine/page-breaks.d.mts +8 -0
  59. package/types/engine/place-kinds.d.mts +18 -0
  60. package/types/engine/settlement-tables.d.mts +25 -158
  61. package/types/engine/settlement-terms.d.mts +53 -0
  62. package/types/engine/sql-tables.d.mts +35 -3
  63. package/types/engine/standing-terms.d.mts +3 -2
  64. package/types/engine/standings.d.mts +14 -2
  65. package/types/engine/unique-items.d.mts +12 -0
  66. package/vendor/markdownlint.mjs +855 -569
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 25.6.0
4
+
5
+ **Changes to act on**
6
+
7
+ - Note queries and a language note's `scope` select notes by property, tag or address; a query that reads `file.path`, `file.folder` or `file.name`, or a scope that lists `paths`, is an error at its position.
8
+ - A scenario no longer takes `data.status`, and a note that still writes it fails the build; mark an unfinished scenario with the `draft` tag instead.
9
+ - A region's settlement table comes from its frontmatter on the website, in Foundry and in books, and a table still written in the body must move there. It needs no holding-model tag, and shows a Held by column only where a row names its holder by address.
10
+
11
+ **New**
12
+
13
+ - Places can be districts, such as a city's quarter or a kingdom's shire, standing within a settlement or a region and falling under its government unless they name their own.
14
+ - Places can be routes, such as a road, a pass or a sea lane, with the journeys along them.
15
+ - An affiliation's page lists who holds each of its offices, drawn from the living beings that name the office, and the content index carries the same list.
16
+ - A being can hold several posts in one body by listing them under `office`.
17
+ - Gear can be marked `unique: true` for a one-of-a-kind item; such an item's note must link a place or a being.
18
+ - A language or spelling guide can declare a written mark of kind `quality`, for a mark that changes which vowel is said rather than how long it is held.
19
+ - A quotation typed with `<<` and `>>` draws no raw-HTML warning.
20
+
3
21
  ## 25.5.5
4
22
 
5
23
  **Languages and scripts**
@@ -17,7 +17,7 @@
17
17
  *
18
18
  * It carries what every package's compiled content needs to read in Foundry as
19
19
  * it does in the preview and on the site: definition lists, verse, alerts,
20
- * inline spoilers, marked names, unresolved links, captioned blocks and
20
+ * inline spoilers, unresolved links, captioned blocks and
21
21
  * highlighted code. Every
22
22
  * class the Foundry renderer emits is styled here or exempted by name in
23
23
  * `tests/base-styles.test.ts`. A package keeps its own stylesheet for its own
package/docs/api.md CHANGED
@@ -496,13 +496,15 @@ What a place is next to (`data.borders`) and reachable from (`data.routes`): the
496
496
 
497
497
  What lies within a place and which affiliation explicitly governs it — read off `data.parents` and resolved explicit `data.government` across this package and every fetched index, inverted into the lists a note's generated **Within**, **Governed by** and **Governed places** sections show — and the population-based government advisory `package-build lint` runs on a place.
498
498
 
499
- | Export | Signature | Returns | Use it when |
500
- | ---------------------- | -------------------------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
501
- | `holdingsNode` | `holdingsNode(fm, { title, url, package, system, canonical })` | `HoldingsNode\|null` | reading a local note as a node of the derivation, or `null` for one that is neither a place nor an affiliation |
502
- | `foreignHoldingsNodes` | `foreignHoldingsNodes(foreignIndex)` | `HoldingsNode[]` | reading a fetched index's places and affiliations as nodes |
503
- | `holdingsPages` | `holdingsPages(nodes, { governmentNodes })` | `Map<string, Holdings>` | inverting `parents` and resolved explicit `government` into each node's sorted lists, keyed by its `url`, for every node with a `url` and an entry |
504
- | `shortcodesOf` | `shortcodesOf(value)` | `string[]` | the shortcodes a `parents` value names — bare or qualified Addresses — empties dropped and repeats collapsed |
505
- | `checkGovernment` | `checkGovernment(note)` | `object[]` | warning at `data.population` for positive population with no own `data.government` key; explicit null means anarchy |
499
+ | Export | Signature | Returns | Use it when |
500
+ | ---------------------------- | -------------------------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
501
+ | `holdingsNode` | `holdingsNode(fm, { title, url, package, system, canonical })` | `HoldingsNode\|null` | reading a local note as a node of the derivation, or `null` for one that is neither a place nor an affiliation |
502
+ | `foreignHoldingsNodes` | `foreignHoldingsNodes(foreignIndex)` | `HoldingsNode[]` | reading a fetched index's places and affiliations as nodes |
503
+ | `holdingsPages` | `holdingsPages(nodes, { governmentNodes })` | `Map<string, Holdings>` | inverting `parents` and resolved explicit `government` into each node's sorted lists, keyed by its `url`, for every node with a `url` and an entry |
504
+ | `shortcodesOf` | `shortcodesOf(value)` | `string[]` | the shortcodes a `parents` value names — bare or qualified Addresses — empties dropped and repeats collapsed |
505
+ | `checkGovernment` | `checkGovernment(note)` | `object[]` | warning at `data.population` for positive population with no `data.government` key, own or inherited; explicit null means anarchy |
506
+ | `inheritDistrictGovernments` | `inheritDistrictGovernments(records, { foreignRecord })` | `void` | giving each district record that states no `data.government` the value of its nearest parent that states one, as the content index does |
507
+ | `governmentInherited` | `governmentInherited(record)` | `boolean` | asking whether a district record's `data.government` came from a parent rather than its own note |
506
508
 
507
509
  An entry carries the listed note's `title`, its `address` in the `note` system, its `type` and `subType`, and its `url` where the note has one. A node with no `url` — a stub — is listed by others and carries no list of its own. Records passed as `governmentNodes` take part in government alone, never in geography.
508
510
 
@@ -531,16 +533,18 @@ What trade a settlement supports (`data.market`): the six-step scale, stated onc
531
533
 
532
534
  ### `engine.settlementTables`
533
535
 
534
- A region's settlements table — a markdown table under `## Settlements` on a `place` note, one row per settlement with its market, its people and its holder — held to its declared columns, to the holding model the region's tags name, and to the notes its rows link. `package-build lint` runs it as part of the `place` check; a note with no such table is not read.
536
+ A region declares its rows in indexed `data.settlements`. The guard checks populations, markets, holders and regional coverage, and refuses authored body tables. Every publishing surface uses the same generated table, retaining the canonical `#settlements` heading or filling the author's existing section.
535
537
 
536
- | Export | Signature | Returns | Use it when |
537
- | ------------------------ | ---------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
538
- | `HOLDING_MODELS` | `const HOLDING_MODELS` | `readonly object[]` | the holding models in order, each its `tag`, the `holder` its Held by cell names, and whether a `person` may hold |
539
- | `SETTLEMENT_COLUMNS` | `const SETTLEMENT_COLUMNS` | `readonly object[]` | the table's columns in header order, each its `name` and whether it is `required` |
540
- | `SETTLEMENTS_HEADING` | `const SETTLEMENTS_HEADING` | `string` | the level-two heading text a settlements table stands under |
541
- | `SETTLEMENT_TABLE_RULES` | `const SETTLEMENT_TABLE_RULES` | `readonly object[]` | the rules in order, each its `name` — the prefix its findings carry — and what it `describe`s |
542
- | `findSettlementTables` | `findSettlementTables(raw)` | `{ heading: number, tables: object[] }` | reading a note's settlements tables, each with its header, its rows' cells and their 1-based lines and columns |
543
- | `checkSettlementTables` | `checkSettlementTables(note, { index })` | `object[]` | linting a place note's settlements tables against the corpus the index holds |
538
+ | Export | Signature | Returns | Use it when |
539
+ | ------------------------- | ------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
540
+ | `SETTLEMENT_COLUMNS` | `const SETTLEMENT_COLUMNS` | `readonly object[]` | the table's columns in header order, each its `name` and whether it is `required`; an optional one appears when a row states it |
541
+ | `SETTLEMENTS_HEADING` | `const SETTLEMENTS_HEADING` | `string` | the generated settlements heading |
542
+ | `SETTLEMENT_TABLE_RULES` | `const SETTLEMENT_TABLE_RULES` | `readonly object[]` | the rules in order, each its `name` — the prefix its findings carry — and what it `describe`s |
543
+ | `settlementMarkdown` | `settlementMarkdown(record)` | `string` | rendering indexed rows as the shared table |
544
+ | `assertSettlementRecords` | `assertSettlementRecords(records, { fileOf, rawOf, foreignIndex })` | `void` | refusing invalid indexed rows before publication |
545
+ | `SETTLEMENTS_FIELD` | `const SETTLEMENTS_FIELD` | `object` | the vocabulary declaration for rows and tail |
546
+ | `SETTLEMENT_ROW_FIELDS` | `const SETTLEMENT_ROW_FIELDS` | `readonly object[]` | the closed row declaration |
547
+ | `checkSettlementTables` | `checkSettlementTables(note, { index })` | `object[]` | linting a place note's settlements tables against the corpus the index holds |
544
548
 
545
549
  ### `engine.mapPlaces`
546
550
 
@@ -1519,6 +1523,10 @@ Naming an interface icon in a note, without drawing it there. The user guide des
1519
1523
 
1520
1524
  Raw HTML in a note's prose, reported. **A note is markdown.** What markdown cannot say, a note does not say — it gets a construct every surface can render, the way `:icon …:` replaces a pasted glyph.
1521
1525
 
1526
+ `HTML_TAG` excludes the doubled `<<` that opens a typed quotation. A quotation
1527
+ written with `<<` and `>>` draws no raw-HTML warning; adjacent real tags retain
1528
+ their findings and positions.
1529
+
1522
1530
  | Export | Signature | Returns | Use it when |
1523
1531
  | ----------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
1524
1532
  | `HTML_TAG` | `const HTML_TAG` | — | A raw HTML tag, opening, closing or self-closing. |
@@ -117,7 +117,7 @@ Write `government: null` for **complete anarchy**. Omitting the key leaves gover
117
117
  | Any | Explicit `null` | None; complete anarchy |
118
118
  | Any | Affiliation Address | None; the reference must resolve |
119
119
 
120
- These rules apply to every place subtype, including settlements, sites, structures, features, regions and worlds. Empty strings, numbers, lists, maps, references to another note type and unresolved references are errors. They do not declare anarchy. `parents`, containment and capitals do not supply a government. The generated **Governed by** and **Governed places** sections use only explicit government references.
120
+ These rules apply to every place subtype, including settlements, sites, structures, features, regions and worlds. Empty strings, numbers, lists, maps, references to another note type and unresolved references are errors. They do not declare anarchy. `parents`, containment and capitals do not supply a government, with one exception: a `district` that omits the key falls under the government of its nearest parent that states one, and is treated as stating that value, so it receives no advisory. An explicit value on the district, `null` included, is its own. The generated **Governed by** and **Governed places** sections use the explicit references and these inherited ones.
121
121
 
122
122
  ## Publication and summaries
123
123
 
@@ -258,8 +258,8 @@ as literal text.
258
258
  Only `---` accepts `.page-break`; `*** {.page-break}` and `___ {.page-break}`
259
259
  are findings. Other classes, ids and attributes on an attributed `---` line
260
260
  are findings too. A page break stands between blocks at the top level of a
261
- note: putting one inside a list, block quote, alert, secret block or fenced
262
- div is a finding at the marker's line and column.
261
+ note: putting one inside a list, block quote, alert, footnote definition,
262
+ secret block or fenced div is a finding at the marker's line and column.
263
263
 
264
264
  ## Captions and numbered references
265
265
 
@@ -1538,13 +1538,14 @@ Nested frontmatter is addressed exactly as a note writes it: `name.full`, `sohl.
1538
1538
 
1539
1539
  The build adds sections to the end of a note, after everything you wrote, listing what other notes say about it. Each appears only where it has something to list, under a fixed anchor:
1540
1540
 
1541
- | Section | Anchor | Lists |
1542
- | ----------------- | ---------------- | -------------------------------------------------------------------- |
1543
- | Within | `within` | on a place, the places whose `parents` name it, grouped by kind |
1544
- | Governed by | `governedby` | on a place, the affiliation its `government` names |
1545
- | Governed places | `governedplaces` | on an affiliation, the places naming it as their `government` |
1546
- | In song and story | `insongandstory` | the works of literature whose `subjects` name the note or its events |
1547
- | From here | `fromhere` | on a place with borders or routes, the map drawn from it |
1541
+ | Section | Anchor | Lists |
1542
+ | ----------------- | ---------------- | ----------------------------------------------------------------------------- |
1543
+ | Within | `within` | on a place, the places whose `parents` name it, grouped by kind |
1544
+ | Governed by | `governedby` | on a place, the affiliation its `government` names |
1545
+ | Governed places | `governedplaces` | on an affiliation, the places naming it as their `government` |
1546
+ | Office holders | `officeholders` | on an affiliation, each office's living holders, from the beings that name it |
1547
+ | In song and story | `insongandstory` | the works of literature whose `subjects` name the note or its events |
1548
+ | From here | `fromhere` | on a place with borders or routes, the map drawn from it |
1548
1549
 
1549
1550
  The event sections below sit among them, and the [format reference](../reference/format-details.md#generated-sections) gives the order. Each entry links to the note it names; a note with no page of its own is named in plain text. Replace a section the way you replace an event view, by writing your own with the same anchor:
1550
1551
 
@@ -96,6 +96,8 @@ markers scoped to each note. Definition lists use a term followed by one or
96
96
  more `: definition` lines. See [footnotes and definition lists](authoring/links-and-markup.md#footnotes-and-definition-lists)
97
97
  for examples and placement in journals, web pages, and books.
98
98
 
99
+ A query selects notes by property, tag or address, never by where a note's file sits: a fence that names `file.path`, `file.folder` or `file.name` is an error at the reference, and a note moved between folders changes no query's answer.
100
+
99
101
  An inline `{{sql "SELECT COUNT(*) FROM notes"}}` expression inserts one scalar query result. Use `{{words (sql "SELECT COUNT(*) FROM notes")}}` for a count in running prose, or `{{digits (sql "SELECT COUNT(*) FROM entries")}}` for grouped numerals. The query must return exactly one row and one column with a nonempty value; a mismatch is a located build error. Prefix `\{{` to show the syntax literally. The [expression reference](reference/format-details.md#dates-and-calendars) describes the helpers and the available SQL relations.
100
102
 
101
103
  Add `gm` to `tags` for a GM-only note. Public site and book builds omit it;
@@ -203,7 +205,44 @@ The [first-note guide](authoring/first-note.md) shows the build commands and how
203
205
 
204
206
  Place notes may declare a governing affiliation with `data.government`, or explicit `null` for complete anarchy. Positive population with an omitted government produces an advisory. See [place governments](authoring/frontmatter.md#place-governments).
205
207
 
206
- A region may list its settlements in a table under `## Settlements` — name, market, people and holder, one row each — tagged with the holding model it is written in. A row links the settlement's note where one exists, and `package-build lint` holds the two to agreement. See [a region's settlements table](reference/format-details.md#a-regions-settlements-table).
208
+ A region declares its table in `data.settlements: { rows, tail }`. The rows
209
+ carry the settlement's `population` and numeric `market`, and either `place`
210
+ (the existing note's Address, default type `place`) or `name` (plain text where
211
+ no note exists), never both. Where the region's culture holds land, a row's
212
+ optional `heldBy` names what holds it by Address (an affiliation or a being,
213
+ default type `affiliation`), and what that note is says how the land is held: a
214
+ temple, a lineage, the city, a kin group. A people who hold no land state no
215
+ `heldBy`, and their table has no Held by column. Optional `for` states why the
216
+ settlement exists. `tail` is the sentence stating what the table leaves out.
217
+
218
+ The guard reads indexed rows. A linked row's population and market agree with
219
+ its note, and every settlement note covered through `parents` has one row in
220
+ the nearest declaring region. `government` states who governs; it never supplies
221
+ who holds the land. HTML, Foundry and Typst render the declared table once.
222
+ A body table under `## Settlements` is an error at its header, naming
223
+ `data.settlements`; no facts are read from it.
224
+
225
+ ```yaml
226
+ data:
227
+ settlements:
228
+ rows:
229
+ - place: ashford
230
+ market: 4
231
+ population: 3400
232
+ heldBy: affiliation-crown
233
+ for: the ford on the drove road
234
+ - name: Weyshott
235
+ market: 2
236
+ population: 420
237
+ heldBy: being-aldwyn
238
+ for: the manor's mill
239
+ - name: Cold Hatch
240
+ market: 1
241
+ population: 60
242
+ tail: Some four hundred further villages lie in the vale, none of them named here.
243
+ ```
244
+
245
+ See [a region's settlements table](reference/format-details.md#a-regions-settlements-table).
207
246
 
208
247
  A being states its gender, frame and appearance in words from one closed vocabulary: `data.gender`, `data.frame`, and `data.appearance.eye_color`, `hair_color`, `skin_color` and `complexion` each take only the words listed for them, and any other value is an error at the value. `complexion` is a list, required once a being states an appearance. `data.appearance.extra_features` lists the being's visible physical marks, each a short plain description of one, and an entry stating a colour, a skin condition or anything that is not a mark is an error. See [a being's look](reference/format-details.md#a-beings-look).
209
248
 
@@ -303,8 +303,8 @@ The optional [book page break](authoring/links-and-markup.md#book-page-breaks)
303
303
  uses only `--- {.page-break}` between blocks at the note's top level.
304
304
  Other classes, ids or attributes on an attributed `---` line receive findings
305
305
  at their opening brace. A `.page-break` on `***` or `___` receives a finding
306
- at the marker. A marker inside a list, block quote, alert, secret block or
307
- fenced div receives `a page break stands between blocks at the top level of a
306
+ at the marker. A marker inside a list, block quote, alert, footnote definition,
307
+ secret block or fenced div receives `a page break stands between blocks at the top level of a
308
308
  note`, at that marker's line and column. Missing surrounding blank lines
309
309
  receive the same placement finding. Code blocks and complete verse divs are
310
310
  literal and receive no page-break findings.
@@ -134,7 +134,10 @@ its language's code. It also has ASCII-folded `nameAscii` and `formsAscii`, the
134
134
  listed forms by language, for searching names typed without diacritics, and
135
135
  `lang`, the language the note is in, derived as
136
136
  [a note's language](../reference/format-details.md#a-notes-language) describes.
137
- A language note that `languages:` names carries its code as `languageCode`. Asset files under `assets/icons`, `assets/images`, and
137
+ A language note that `languages:` names carries its code as `languageCode`.
138
+ An affiliation carries `officeHolders`, each of its offices that a living
139
+ being holds with those beings' Addresses, derived as
140
+ [office holders](../reference/format-details.md#type-affiliation) describes. Asset files under `assets/icons`, `assets/images`, and
138
141
  `assets/audio` have records in the same index, identified by an `asset` block.
139
142
  The index is disposable build output; regenerate it from the authored tree
140
143
  when the content changes.
@@ -244,7 +244,7 @@ notes' sources. A page is reachable when an authored link leads to it:
244
244
  The links the build generates do not make a page reachable. These are the
245
245
  Related card, the previous and next links, the infobox links, and the sections
246
246
  generated after a note's text (Within, Governed by, Governed places,
247
- Chronology, Events, Accounts, What followed, In song and story). Each is drawn
247
+ Office holders, Chronology, Events, Accounts, What followed, In song and story). Each is drawn
248
248
  without a link to a page outside the reachable set, and a section left with
249
249
  nothing under it is not drawn. An infobox value that names an unreachable page
250
250
  is set as plain text. So is a link in a dictionary or glossary entry, whether