@heroiclands/package-build 19.0.0 → 20.0.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 (65) hide show
  1. package/CHANGELOG.md +624 -0
  2. package/CONTENT.md +79 -8
  3. package/bin/content-build.mjs +7 -1
  4. package/content-config.mjs +10 -1
  5. package/docs/content-format.md +394 -72
  6. package/engine/actor-compiler.mjs +197 -7
  7. package/engine/address-charset.mjs +23 -5
  8. package/engine/base-compiler.mjs +63 -2
  9. package/engine/bundles.mjs +9 -0
  10. package/engine/content-address.mjs +92 -1
  11. package/engine/content-format.mjs +102 -0
  12. package/engine/content-index.mjs +11 -8
  13. package/engine/content-links.mjs +37 -21
  14. package/engine/field-reference.mjs +57 -5
  15. package/engine/field-spec.mjs +214 -7
  16. package/engine/folder-notes.mjs +24 -1
  17. package/engine/foreign-catalog.mjs +4 -1
  18. package/engine/foundry-entries.mjs +14 -0
  19. package/engine/frontmatter-lint.mjs +186 -27
  20. package/engine/frontmatter.mjs +11 -11
  21. package/engine/generate.mjs +67 -10
  22. package/engine/helpers.mjs +86 -9
  23. package/engine/index.mjs +3 -0
  24. package/engine/item-compiler.mjs +37 -0
  25. package/engine/journals.mjs +21 -4
  26. package/engine/macros.mjs +8 -0
  27. package/engine/map-notes.mjs +7 -7
  28. package/engine/note-ids.mjs +25 -1
  29. package/engine/note-vocabulary.mjs +76 -9
  30. package/engine/retired-fields.mjs +57 -16
  31. package/engine/runtime-only-fields.mjs +204 -0
  32. package/engine/scenes.mjs +12 -19
  33. package/engine/schema-check.mjs +23 -1
  34. package/engine/site-index.mjs +17 -0
  35. package/engine/subtype-registry.mjs +30 -0
  36. package/engine/system-block.mjs +81 -3
  37. package/engine/web-wikilinks.mjs +33 -27
  38. package/engine/wikilink-syntax.mjs +7 -0
  39. package/engine/wikilinks.mjs +74 -16
  40. package/hm3/actors.mjs +63 -13
  41. package/package.json +2 -2
  42. package/sohl/actors.mjs +106 -7
  43. package/sohl/item-fields.mjs +203 -0
  44. package/sohl/note-schemas.mjs +6 -3
  45. package/types/engine/actor-compiler.d.mts +83 -3
  46. package/types/engine/address-charset.d.mts +22 -4
  47. package/types/engine/base-compiler.d.mts +54 -3
  48. package/types/engine/content-address.d.mts +64 -0
  49. package/types/engine/content-format.d.mts +9 -0
  50. package/types/engine/field-spec.d.mts +271 -3
  51. package/types/engine/folder-notes.d.mts +20 -0
  52. package/types/engine/foundry-entries.d.mts +6 -0
  53. package/types/engine/frontmatter-lint.d.mts +18 -2
  54. package/types/engine/frontmatter.d.mts +11 -11
  55. package/types/engine/generate.d.mts +27 -0
  56. package/types/engine/helpers.d.mts +37 -9
  57. package/types/engine/index.d.mts +1 -0
  58. package/types/engine/map-notes.d.mts +2 -2
  59. package/types/engine/note-ids.d.mts +14 -0
  60. package/types/engine/retired-fields.d.mts +29 -13
  61. package/types/engine/runtime-only-fields.d.mts +102 -0
  62. package/types/engine/schema-check.d.mts +10 -1
  63. package/types/engine/subtype-registry.d.mts +21 -0
  64. package/types/engine/system-block.d.mts +28 -2
  65. package/types/sohl/actors.d.mts +3 -3
package/CONTENT.md CHANGED
@@ -388,6 +388,24 @@ exactly once, here, and read through `loadPackConfig()` everywhere. There is one
388
388
  resolved set at runtime; the compilers and the link-manifest emitter cannot come
389
389
  to disagree about which notes carry documentation.
390
390
 
391
+ **Every note compiling into a system-bearing document carries documentation, and
392
+ that includes actors** (#337). `docEntryTypes` is `itemTypes` plus the actor
393
+ types the shipped subtype maps declare (`ACTOR_TYPES`, derived from them rather
394
+ than listed again), plus `macro` and the map types. Only `doc` is outside it, for
395
+ the reason that actually applies to it: its single document _is_ the prose.
396
+
397
+ A being therefore has the same two addresses an item has — `<pkg>-<system>-being-<shortcode>`
398
+ for the Actor and `<pkg>-none-docbeing-<shortcode>` for the page — where before
399
+ it had only the first, and was the one system-bearing note a prose link could not
400
+ name.
401
+
402
+ **An actor keeps its prose inline as well, and that asymmetry is deliberate.** An
403
+ item's description is an `@UUID` pointer into its journal, because one item is
404
+ embedded across hundreds of beings and baking long prose into every copy bloats
405
+ the compendium by the length of the text times the number of carriers. An actor
406
+ is singular, so the same indirection costs a reader a click and saves nothing:
407
+ `system.appearance` and `system.dossier` stay as rendered prose.
408
+
391
409
  The Item compiler **dispatches through that same resolved table**, via
392
410
  `engine/item-registry.mjs` (`itemTypes()` and `itemBuilder(type)`), so the types a
393
411
  consumer's notes are accepted for and the builders they compile with are one
@@ -534,9 +552,46 @@ Both spellings are equal; the difference is only whether the type brings art.
534
552
  whitelist without a builder behind it.
535
553
 
536
554
  **The path is spelled the way a note spells it.** Registry art goes through the
537
- same `resolveImg` rule as a note's `img:`, so `icons/relic.svg` means _this_
538
- repository's asset root — `modules/sohl-relics/assets/icons/relic.svg` — and an
539
- already-served path (`systems/sohl/assets/icons/…`) passes through untouched.
555
+ same `resolveImg` rule as a note's `img:`, so one spelling means one thing
556
+ wherever it is written.
557
+
558
+ #### An asset path's first segment says which package owns it
559
+
560
+ Every authored asset path — a registry `img:`, a note's `img:`, an actor's
561
+ `portrait:` — answers "which package holds this file?" in its **first segment**,
562
+ and there are exactly three answers:
563
+
564
+ | Authored path starts with | Owner | Emitted |
565
+ | ------------------------- | --------------------- | -------------------- |
566
+ | `systems/` | a separate **system** | unchanged |
567
+ | `modules/` | a separate **module** | unchanged |
568
+ | anything else | **this package** | `<assetRoot>/<path>` |
569
+
570
+ `assetRoot` is derived, never authored: it is
571
+ `<packageKind>/<foundryPackage>/assets`, and it is the one place `systems/sohl`
572
+ (or `modules/sohl-thalorna`) is ever spelled. So `icons/relic.svg` in a module's
573
+ registry compiles to `modules/sohl-relics/assets/icons/relic.svg`, and the same
574
+ string in the system's compiles to `systems/sohl/assets/icons/relic.svg`. An
575
+ already-served `systems/sohl/assets/icons/…` passes through untouched — which is
576
+ what lets a module pair a SoHL default with one of its own types.
577
+
578
+ **"Anything else" is the rule, not a list of directories.** A package owns its
579
+ whole `assets/` tree, so a directory this toolchain has never heard of is still
580
+ that package's: art under `assets/artwork/` is addressed `artwork/deity.webp`
581
+ and rooted exactly as `icons/…` and `images/…` are. An address naming no package
582
+ at all — an absolute URL, a `data:` URI, a `/`-rooted path — passes through, on
583
+ the same rule rather than as an exception.
584
+
585
+ `worlds/` is deliberately not exempt: a package may not ship art out of a world,
586
+ so prefixing such a path produces a plainly broken one rather than a plausible
587
+ one that 404s in Foundry unreported.
588
+
589
+ **`banner:` is a path that does not follow this rule.** It reaches no compiled
590
+ document; it is a top-level key the Hugo theme reads, and the theme prefixes a
591
+ relative value with `images/` and joins it onto `params.cdnBaseURL`. The two
592
+ address different places — `img:` a file Foundry serves, `banner:` a file the
593
+ CDN serves — so they are stated apart rather than reconciled. See the
594
+ [content format specification](docs/content-format.md#banner-addresses-the-cdn-not-the-foundry-install).
540
595
 
541
596
  #### "Names no art" and "wants no art" are different (#218)
542
597
 
@@ -570,12 +625,20 @@ rather than two.
570
625
  > (`sohl/item-fields.mjs`, "the style of address the office carries"), so one
571
626
  > authored key fed two unrelated destinations that disagreed about what empty
572
627
  > means — and `title: null` stringified into the compiled document as the literal
573
- > `"null"`. That collision is gone: the field declares `topLevelMeans`, and the
574
- > top-level key is no longer a source for it (#218).
628
+ > `"null"`. The field declares `topLevelMeans` now, so the top-level key is no
629
+ > longer a source for it (#218).
630
+ >
631
+ > So `title: null` is a note declining to state a heading, and the site emitter's
632
+ > `fm.title ?? name` falls back to `name.full`. `title: ""` publishes a
633
+ > deliberately blank heading and is warned about on its own account — as the
634
+ > page's heading, not as an art path.
575
635
  >
576
- > So `title: null` is now a note declining to state a heading, and the site
577
- > emitter's `fm.title ?? name` falls back to `name.full`. `title: ""` still
578
- > publishes a deliberately blank heading, and nothing warns about that yet.
636
+ > **That warning reads the note's top level only.** The two spellings still name
637
+ > unrelated quantities, so a `sohl.title` the office's style of address must
638
+ > not answer for the page's heading. Twenty-eight `sohl-kethira-basic`
639
+ > affiliations write `sohl.title: ""`, meaning an office with no style of
640
+ > address, and every one of them was reported as publishing a blank heading
641
+ > until the check honoured the declaration (#312).
579
642
 
580
643
  Because `""` used to mean "unset", a note still carrying that spelling has
581
644
  quietly changed meaning, and the frontmatter lint says so — for either art
@@ -712,6 +775,14 @@ state the same quantity, which is nearly everywhere: `subType` is the other
712
775
  declared item field spelled like a note-level key, and there the two agree by
713
776
  design.
714
777
 
778
+ **The statement is symmetric, and the frontmatter linter reads it from the other
779
+ side too.** If the two positions hold unrelated quantities, then the _in-block_
780
+ position is not the note-level field either — so a check about a note-level field
781
+ (the page's heading, an art path) reads past a block key the note's own type
782
+ claims for something else. Declaring `topLevelMeans` settles both directions at
783
+ once; it was read for the emitted field alone until #312, which is how an
784
+ affiliation's office style came to answer for its page heading.
785
+
715
786
  **An exempted field is still authorable**, at the two positions that describe the
716
787
  document rather than the note:
717
788
 
@@ -136,7 +136,7 @@ import {
136
136
  locateAddressFinding,
137
137
  addressFindingMessage,
138
138
  } from "../engine/address-diff.mjs";
139
- import { itemPackJsonDirs } from "../engine/generate.mjs";
139
+ import { emittedArtFor, itemPackJsonDirs } from "../engine/generate.mjs";
140
140
 
141
141
  /**
142
142
  * The packs `unpack` extracts.
@@ -799,6 +799,12 @@ function lintCommand() {
799
799
  // the compile asks the map for whichever pack it is
800
800
  // writing, and a companion is a pack it writes.
801
801
  packs: config.packDirectories,
802
+ // What art each type actually reaches its document through,
803
+ // asked of the passes rather than listed here (#349). This
804
+ // is the one place that decides what a tree is held to, so
805
+ // it is where the derivation is handed over — the linter
806
+ // states no list of iconless types of its own.
807
+ emittedArt: emittedArtFor,
802
808
  references: argv.references,
803
809
  });
804
810
 
@@ -69,6 +69,7 @@ import path from "node:path";
69
69
  // a cycle around a consumer's config file (see `engine/pack-config.mjs`).
70
70
  import { ADDRESS_SEGMENT_PATTERN, isAddressSegment } from "./engine/address-charset.mjs";
71
71
  import { MAP_TYPES, PACK_BY_TYPE } from "./engine/ids.mjs";
72
+ import { ACTOR_TYPES } from "./engine/subtype-registry.mjs";
72
73
  import { NOTE_VOCABULARY } from "./engine/note-vocabulary.mjs";
73
74
 
74
75
  /**
@@ -1953,7 +1954,15 @@ export function defineConfig(config) {
1953
1954
  // holds every key any of them declares, so this stays "the registry's keys"
1954
1955
  // rather than becoming a second list to keep in step (#1504).
1955
1956
  const itemTypes = Object.freeze(new Set(Object.keys(itemBuilders)));
1956
- const docEntryTypes = Object.freeze(new Set([...itemTypes, "macro", ...MAP_TYPES]));
1957
+ // Every note that compiles into a *system-bearing* document publishes its
1958
+ // prose as a documentation JournalEntry, and that includes actors (#337).
1959
+ // A being was the one such note with no `none` address — its only address
1960
+ // named the Actor — so nothing a prose link wrote could land on its page.
1961
+ // `doc` stays out for the reason that actually applies to it: its single
1962
+ // document *is* the prose.
1963
+ const docEntryTypes = Object.freeze(
1964
+ new Set([...itemTypes, ...ACTOR_TYPES, "macro", ...MAP_TYPES]),
1965
+ );
1957
1966
 
1958
1967
  return Object.freeze({
1959
1968
  rootDir,