@heroiclands/package-build 22.14.4 → 22.15.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 (60) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/CONTENT.md +25 -1
  3. package/bin/content-build.mjs +3 -7
  4. package/bin/package-build.mjs +25 -0
  5. package/docs/api.md +61 -34
  6. package/docs/commands.md +65 -9
  7. package/docs/configuration.md +6 -3
  8. package/docs/content-format.md +165 -121
  9. package/docs/diagnostics.md +1 -1
  10. package/docs/getting-started.md +23 -0
  11. package/engine/base-compiler.mjs +3 -1
  12. package/engine/being-age.mjs +16 -24
  13. package/engine/calendar-artifacts.mjs +153 -0
  14. package/engine/calendar-notes.mjs +64 -16
  15. package/engine/calendars.mjs +129 -0
  16. package/engine/content-index.mjs +3 -1
  17. package/engine/exported-scene.mjs +230 -0
  18. package/engine/generate.mjs +1 -0
  19. package/engine/ids.mjs +1 -1
  20. package/engine/index.mjs +6 -0
  21. package/engine/infobox.mjs +4 -0
  22. package/engine/itinerary-scenes.mjs +170 -0
  23. package/engine/map-build.mjs +10 -45
  24. package/engine/map-dot.mjs +6 -3
  25. package/engine/map-graphviz.mjs +50 -131
  26. package/engine/map-notes.mjs +26 -3
  27. package/engine/map-places.mjs +12 -6
  28. package/engine/map-raster.mjs +49 -0
  29. package/engine/note-dates.mjs +125 -85
  30. package/engine/note-vocabulary.mjs +22 -6
  31. package/engine/pdf-build.mjs +57 -1
  32. package/engine/pdf-render.mjs +18 -0
  33. package/engine/reckoning-markers.mjs +207 -0
  34. package/engine/scenes.mjs +113 -26
  35. package/engine/site-build.mjs +11 -13
  36. package/engine/site-maps.mjs +5 -35
  37. package/engine/site-preview.mjs +6 -1
  38. package/engine/social-tie-terms.mjs +3 -0
  39. package/engine/social-ties.mjs +30 -19
  40. package/package.json +3 -1
  41. package/types/engine/being-age.d.mts +2 -2
  42. package/types/engine/calendar-artifacts.d.mts +16 -0
  43. package/types/engine/calendar-notes.d.mts +1 -1
  44. package/types/engine/calendars.d.mts +65 -0
  45. package/types/engine/exported-scene.d.mts +12 -0
  46. package/types/engine/index.d.mts +2 -0
  47. package/types/engine/itinerary-scenes.d.mts +29 -0
  48. package/types/engine/map-build.d.mts +4 -13
  49. package/types/engine/map-graphviz.d.mts +17 -59
  50. package/types/engine/map-places.d.mts +5 -1
  51. package/types/engine/map-raster.d.mts +8 -0
  52. package/types/engine/note-dates.d.mts +6 -0
  53. package/types/engine/note-vocabulary.d.mts +3 -3
  54. package/types/engine/pdf-render.d.mts +7 -1
  55. package/types/engine/reckoning-markers.d.mts +27 -0
  56. package/types/engine/scenes.d.mts +9 -2
  57. package/types/engine/site-build.d.mts +1 -5
  58. package/types/engine/site-maps.d.mts +2 -7
  59. package/types/engine/social-tie-terms.d.mts +2 -0
  60. package/types/engine/social-ties.d.mts +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 22.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ **Place maps** — Site pages, books, and Foundry itinerary Scenes render their maps with the installed build package, without a separate Graphviz installation.
8
+
9
+ **Regional maps** — Foundry stages authored SVG backgrounds as PNGs without a separate SVG converter.
10
+
11
+ **Content relationships**
12
+
13
+ - A being's social ties now reject two entries naming the same target, even when one uses a shorter address.
14
+
15
+ ### Patch Changes
16
+
17
+ **Content dates**
18
+
19
+ - Notes can name a calendar era with a short marker such as `VR(720/5/14)`, while canonical dates use year and day of year.
20
+
21
+ **Place maps** — Books give mapped places a full-page itinerary, and Foundry offers gridless itinerary Scenes with linked place journals.
22
+
23
+ **Regional maps** — Hand-drawn SVG charts appear in books and use raster backgrounds in Foundry while retaining their stated scale.
24
+
25
+ **Scenes** — Battle and local maps can use full Foundry Scene exports, with marked pins opening their note's journal pages and asset fixups resolving paths.
26
+
27
+ **Theatre of the mind** — Image-led maps can create gridless Scenes for narration-focused play.
28
+
3
29
  ## 22.14.4
4
30
 
5
31
  ### Patch Changes
package/CONTENT.md CHANGED
@@ -1277,7 +1277,9 @@ is from a closed set: the eight bearings, `land | boat | ship`, a days scale of
1277
1277
  terrain names the modes that cross it. Every `to` must be the shortcode of a
1278
1278
  place, here or in a fetched index. The other end must state the pair back —
1279
1279
  at the opposite bearing, and for a route by the same mode in the same days.
1280
- A neighbour stating nothing is a warning naming both notes; one stating a
1280
+ A settlement states a border only when it shares a frontier; most settlements
1281
+ use `parents` for location and `routes` for journeys. The terrain registry is
1282
+ shared across packages. A neighbour stating nothing is a warning naming both notes; one stating a
1281
1283
  different bearing, mode or days is an error, as is a border to the note's own
1282
1284
  parent or child, or a pair listed twice by one mode. Each finding lands on the
1283
1285
  entry that states it. The keys, the sets and the days scale are specified under
@@ -2683,6 +2685,24 @@ What the build guarantees:
2683
2685
  `map` is derived, never authored: a note that writes one has it replaced when
2684
2686
  the build draws a map, and dropped when there is none to draw.
2685
2687
 
2688
+ The same relations supply a PDF book's full-page vector map immediately after
2689
+ each selected place entry. When a Scene pack is configured, they also supply
2690
+ gridless Foundry Scenes with raster backgrounds and pins to journals in an
2691
+ Adventure. A place from a dependency receives a pin through an importable
2692
+ journal link when its published index includes a JournalEntry UUID. An
2693
+ authored `regionalmap` with SVG art keeps the SVG for site and book while the
2694
+ Foundry build stages a PNG. Its `data.scale` states the distance and unit per
2695
+ grid unit.
2696
+
2697
+ Battle and local maps can carry a Foundry Scene export in `data.scene`. The
2698
+ Scene's canvas and embedded documents stay as authored. A pin whose text is
2699
+ `#anchor` opens the JournalEntry page with that heading in the map note and
2700
+ displays the heading text. A `totm` map uses a background image and dimensions
2701
+ to create a gridless Scene for theatre-of-the-mind play.
2702
+ An exported Scene can declare `data.fixup` entries that resolve asset Addresses
2703
+ into specific Scene fields. A fixup path can select an embedded document by its
2704
+ stable `_id`, so reordering the export does not redirect the replacement.
2705
+
2686
2706
  ### Why the output location is fixed
2687
2707
 
2688
2708
  The content mount is a build artifact and is **deleted on every run**, so that a
@@ -2803,6 +2823,10 @@ columns is set across the page. Two columns are print's answer and print's
2803
2823
  alone — a scrolling page has no fixed viewport, so the website keeps one
2804
2824
  measure with a side rail.
2805
2825
 
2826
+ An itinerary map uses a separate full-width page after its place entry. The
2827
+ vector drawing fits the printable area and carries a heading naming the
2828
+ viewpoint. A selected authored regional SVG map takes a page in the same way.
2829
+
2806
2830
  A note's `description:` sets as an epigraph between short rules under the
2807
2831
  plate. It disappears rather than leave a shell: a note with no description has
2808
2832
  no epigraph. The running foot carries the section's name, an ornament and the
@@ -1531,7 +1531,7 @@ function pdfCommand() {
1531
1531
  * wikilink resolution, code-fence protection and the foreign-manifest merge —
1532
1532
  * happens here, from configuration. So does the Hugo configuration: the whole
1533
1533
  * source tree Hugo reads lands under `build/hugo/`, and the consumer's script
1534
- * runs Hugo over it. With `site.maps` on and GraphViz installed, every place
1534
+ * runs Hugo over it. With `site.maps` on, every place
1535
1535
  * page with a border or a route carries the map from that place.
1536
1536
  *
1537
1537
  * **The Hugo configuration is generated before anything is written.** Its
@@ -1674,10 +1674,7 @@ function siteCommand() {
1674
1674
  return;
1675
1675
  }
1676
1676
 
1677
- // What drawing the maps found: warnings, every one. A site
1678
- // never fails for want of a map, so none of these touches the
1679
- // exit code — GraphViz absent is one line naming what to
1680
- // install, and every page is written as it would be without.
1677
+ // Relation warnings do not fail a site build.
1681
1678
  for (const f of result.mapFindings) emitDiagnostic(f);
1682
1679
 
1683
1680
  const s = result.stats;
@@ -1707,8 +1704,7 @@ function siteCommand() {
1707
1704
  * check, whose anomalies are reported as findings), the map from a place
1708
1705
  * from its `borders` and `routes` (`--from`), the chart from a place — the
1709
1706
  * same drawing at a larger horizon (`--chart`, `--horizon`) — and the whole
1710
- * route graph (`--travel`). GraphViz draws; when it is absent the command
1711
- * says what to install and stops.
1707
+ * route graph (`--travel`). The installed Graphviz runtime draws each map.
1712
1708
  *
1713
1709
  * @returns {object} The yargs command module.
1714
1710
  */
@@ -49,6 +49,7 @@
49
49
  * Usage:
50
50
  * npx package-build clean [--distclean]
51
51
  * npx package-build assets
52
+ * npx package-build calendars --calendaria-version <version>
52
53
  * npx package-build manifest
53
54
  * npx package-build lang check
54
55
  * npx package-build lang coverage [--unused]
@@ -88,6 +89,7 @@ import { compilesFoundryDocuments } from "../content-config.mjs";
88
89
  import { loadPackConfig, packConfigPath, resolveConfigFile } from "../engine/pack-config.mjs";
89
90
  import { cleanBuildArtifacts, stageAssets } from "../stage.mjs";
90
91
  import { buildSchemaArtifact } from "../engine/schema-extract.mjs";
92
+ import { emitCalendarArtifacts } from "../engine/calendar-artifacts.mjs";
91
93
  import { SCHEMA_ARTIFACT_FILE } from "../engine/foreign-catalog.mjs";
92
94
  import { validateLangSource } from "../lang.mjs";
93
95
  import { checkLabelRegistry } from "../labels.mjs";
@@ -316,6 +318,28 @@ function assetsCommand() {
316
318
  };
317
319
  }
318
320
 
321
+ /** Emit the package's calendar definitions from its content index. */
322
+ function calendarsCommand() {
323
+ return {
324
+ command: "calendars",
325
+ describe: "Emit calendar definitions and Calendaria import files",
326
+ builder: (y) =>
327
+ y.option("calendaria-version", {
328
+ type: "string",
329
+ demandOption: true,
330
+ describe: "Calendaria import format version",
331
+ }),
332
+ handler: handler((args) => {
333
+ const config = loadPackConfig();
334
+ const result = emitCalendarArtifacts({
335
+ config,
336
+ calendariaVersion: args.calendariaVersion,
337
+ });
338
+ console.log(`✅ ${result.calendars} calendars emitted (${result.files} files).`);
339
+ }),
340
+ };
341
+ }
342
+
319
343
  /**
320
344
  * Format generated text the way the repository formats everything else.
321
345
  *
@@ -1386,6 +1410,7 @@ yargs(hideBin(process.argv))
1386
1410
  .scriptName("package-build")
1387
1411
  .command(cleanCommand())
1388
1412
  .command(assetsCommand())
1413
+ .command(calendarsCommand())
1389
1414
  .command(manifestCommand())
1390
1415
  .command(siteRootCommand())
1391
1416
  .command(schemaCommand())
package/docs/api.md CHANGED
@@ -558,36 +558,33 @@ The drawings of `content-build map` as DOT text, sharing one style table: shape,
558
558
 
559
559
  ### `engine.mapGraphviz`
560
560
 
561
- GraphViz, as the map command reaches it: a build-time tool of that command alone, found once, named plainly when absent, and run over a `.dot` file the command keeps beside the rendering.
561
+ The npm-installed Graphviz runtime draws SVG maps and supplies the plain positions used by Foundry itinerary pins. PNG backgrounds are rasterized from the same SVG layout. The `.dot` source stays beside each rendering.
562
562
 
563
- | Export | Signature | Returns | Use it when |
564
- | ------------------------ | ------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
565
- | `GRAPHVIZ_ENGINES` | `const GRAPHVIZ_ENGINES` | `readonly string[]` | the engines the map command may ask for — `dot`, `twopi`, `neato` |
566
- | `GRAPHVIZ_INSTALL` | `const GRAPHVIZ_INSTALL` | `string` | what installs GraphViz, as the clause every "not installed" message ends with |
567
- | `findGraphviz` | `findGraphviz(engine, { env, fallbacks })` | `string\|undefined` | finding an engine's binary on `PATH` or in the directories a package manager installs into |
568
- | `graphvizMissingMessage` | `graphvizMissingMessage(engine)` | `string` | saying an engine is not installed, and what installs it |
569
- | `renderDot` | `renderDot(dotPath, outPath, { binary, format, args })` | `{warnings}` | rendering a `.dot` file, the engine's warnings returned and a non-zero exit thrown |
563
+ | Export | Signature | Returns | Use it when |
564
+ | ------------------ | ----------------------------------------------------------- | ------------------- | ------------------------------------------------------ |
565
+ | `GRAPHVIZ_ENGINES` | `const GRAPHVIZ_ENGINES` | `readonly string[]` | choosing `dot`, `twopi`, or `neato` |
566
+ | `renderDot` | `renderDot(dotPath, outPath, { engine, format, nop, dpi })` | `{warnings}` | rendering a DOT source to SVG, plain positions, or PNG |
570
567
 
571
568
  ### `engine.mapBuild`
572
569
 
573
- `content-build map` as a function: the drawings written under an output directory, always the `.dot` beside the `.svg`, the tree's anomalies returned as findings, and a missing GraphViz either an error or — for a build that would rather skip — one warning.
570
+ `content-build map` as a function: drawings written under an output directory, with each `.dot` beside its `.svg` and tree anomalies returned as findings.
574
571
 
575
- | Export | Signature | Returns | Use it when |
576
- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ----------------------------------------------------------------------------- |
577
- | `MAP_DIR` | `const MAP_DIR` | `string` | where the maps land below a package's root, `build/map` |
578
- | `FROM_ALL` | `const FROM_ALL` | `string` | the `--from` or `--chart` value that means every place with a relation, `all` |
579
- | `relatedPlaces` | `relatedPlaces(places)` | `string[]` | every place that states, or is named in, a border or a route |
580
- | `buildMaps` | `buildMaps({ world, outDir, tree, root, from, chart, horizon, travel, engine, rankdir, nodesep, ranksep, scale, polities, locate, requireGraphviz })` | `{written, findings, skipped}` | drawing any of the four from a loaded world |
572
+ | Export | Signature | Returns | Use it when |
573
+ | --------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------------------------------------------------------------------------- |
574
+ | `MAP_DIR` | `const MAP_DIR` | `string` | where the maps land below a package's root, `build/map` |
575
+ | `FROM_ALL` | `const FROM_ALL` | `string` | the `--from` or `--chart` value that means every place with a relation, `all` |
576
+ | `relatedPlaces` | `relatedPlaces(places)` | `string[]` | every place that states, or is named in, a border or a route |
577
+ | `buildMaps` | `buildMaps({ world, outDir, tree, root, from, chart, horizon, travel, engine, rankdir, nodesep, ranksep, scale, polities })` | `{written, findings}` | drawing any of the four from a loaded world |
581
578
 
582
579
  ### `engine.siteMaps`
583
580
 
584
- The map from each place, drawn for the site: `content-build site` draws the map from every place of this package that states, or is named in, a border or a route — with the site's base, so every name links through it — and the page writer lays each drawing beside its page as `from-<shortcode>.svg` and names it in the front matter as `map`. GraphViz absent is one warning and no map; `site.maps: false` draws none.
581
+ The map from each place, drawn for the site: `content-build site` draws the map from every place of this package that states, or is named in, a border or a route — with the site's base, so every name links through it — and the page writer lays each drawing beside its page as `from-<shortcode>.svg` and names it in the front matter as `map`. `site.maps: false` draws none.
585
582
 
586
- | Export | Signature | Returns | Use it when |
587
- | -------------- | --------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------- |
588
- | `SITE_MAP_DIR` | `const SITE_MAP_DIR` | `string` | where the site build draws below a package's root, `build/map/site` |
589
- | `inlineSvg` | `inlineSvg(svg)` | `string` | rewriting a GraphViz SVG for inlining — no prologue, no comment, no `xlink:`, the root sized by `viewBox` alone |
590
- | `drawSiteMaps` | `drawSiteMaps({ records, foreignIndex, config, base, locate })` | `{maps, findings}` | drawing the map from every related place of this package, keyed by the URL of the page that carries it |
583
+ | Export | Signature | Returns | Use it when |
584
+ | -------------- | ------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------- |
585
+ | `SITE_MAP_DIR` | `const SITE_MAP_DIR` | `string` | where the site build draws below a package's root, `build/map/site` |
586
+ | `inlineSvg` | `inlineSvg(svg)` | `string` | rewriting a GraphViz SVG for inlining — no prologue, no comment, no `xlink:`, the root sized by `viewBox` alone |
587
+ | `drawSiteMaps` | `drawSiteMaps({ records, foreignIndex, config, base })` | `{maps, findings}` | drawing the map from every related place of this package, keyed by the URL of the page that carries it |
591
588
 
592
589
  ### `engine.systems`
593
590
 
@@ -609,21 +606,28 @@ The closed registry of system ids, and the `none` that stands for no system at a
609
606
 
610
607
  The axis every date normalises onto, the arithmetic over a calendar's month list, and the moon. A reckoning is an **era** declared on the note about the calendar that counts those years, and its epoch is the `start` written on that row — nothing is registered in configuration. The year conversion is piecewise on the sign, because the authored numbering has no year zero while the normalised value does: authored `-1` is `0`, and `-1` and `1` are adjacent. `lunarPhase` takes a floored modulo for the same reason the conversion branches: a truncated one is right on one side of an epoch and wrong on the other.
611
608
 
612
- | Export | Signature | Returns | Use it when |
613
- | ------------------- | ---------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------- |
614
- | `CANONICAL_EPOCH` | `const CANONICAL_EPOCH` | — | naming where the canonical axis's own year 1 sits |
615
- | `canonicalYear` | `canonicalYear(year, epoch?)` | `number` — the signed year on the axis | converting a year written in one reckoning to the number everything compares on |
616
- | `dateSortKey` | `dateSortKey(year, month, day)` | `number` | ordering mixed-precision dates against a single number |
617
- | `calendarStructure` | `calendarStructure(calendar)` | `{months}` — `null` where unstated | reading the months a calendar keeps, before phrasing a finding |
618
- | `daysInMonth` | `daysInMonth(calendar, month)` | `number` — `null` where unstated | bounding a written day against the month it names |
619
- | `daysInYear` | `daysInYear(months)` | `number` | asking what a month list claims the year is |
620
- | `monthStarts` | `monthStarts(months)` | `number[]` | reading the same sum as a sequence, which is what an ordering error moves |
621
- | `dayOfYear` | `dayOfYear(months, month, day)` | `number` | placing a written month and day in the year |
622
- | `lunarPhase` | `lunarPhase(day, epochDay, cycle)` | `number` — `0` to `cycle - 1` | asking how far into its cycle a moon is, on either side of the epoch |
609
+ The neutral day spelling is `<year>.<day>[:HHMMSS]`. The pure conversion functions take the world's year length and an explicit origin year, so their arithmetic names no month or era. A caller supplies those values from its content; no setting year is baked into the toolchain.
610
+
611
+ | Export | Signature | Returns | Use it when |
612
+ | ------------------------- | ---------------------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------- |
613
+ | `CANONICAL_EPOCH` | `const CANONICAL_EPOCH` | — | naming where the canonical axis's own year 1 sits |
614
+ | `canonicalYear` | `canonicalYear(year, epoch?)` | `number` — the signed year on the axis | converting a year written in one reckoning to the number everything compares on |
615
+ | `dateSortKey` | `dateSortKey(year, month, day)` | `number` | ordering mixed-precision dates against a single number |
616
+ | `calendarStructure` | `calendarStructure(calendar)` | `{months}` — `null` where unstated | reading the months a calendar keeps, before phrasing a finding |
617
+ | `daysInMonth` | `daysInMonth(calendar, month)` | `number` — `null` where unstated | bounding a written day against the month it names |
618
+ | `daysInYear` | `daysInYear(months)` | `number` | asking what a month list claims the year is |
619
+ | `monthStarts` | `monthStarts(months)` | `number[]` | reading the same sum as a sequence, which is what an ordering error moves |
620
+ | `dayOfYear` | `dayOfYear(months, month, day)` | `number` | placing a written month and day in the year |
621
+ | `monthDayOfYear` | `monthDayOfYear(months, ordinal)` | `{month, day}` or `null` | reading an ordinal day in a calendar's month layout |
622
+ | `lunarPhase` | `lunarPhase(day, epochDay, cycle)` | `number` — `0` to `cycle - 1` | asking how far into its cycle a moon is, on either side of the epoch |
623
+ | `canonicalDayOffset` | `canonicalDayOffset(year, day, originYear, daysPerYear)` | signed whole-day offset | placing a canonical date against a chosen origin |
624
+ | `canonicalDateFromOffset` | `canonicalDateFromOffset(offset, originYear, daysPerYear)` | `{year, day}` | recovering a canonical date, including days before the origin |
625
+ | `formatCanonicalDate` | `formatCanonicalDate(date, daysPerYear)` | canonical date string | printing a day and optional 24-hour time without calendar months |
626
+ | `parseCanonicalDate` | `parseCanonicalDate(value, daysPerYear)` | `{year, day, seconds?}` or `null` | reading that spelling without accepting a month date |
623
627
 
624
628
  ### `engine.noteDates`
625
629
 
626
- A date on a note is one authored string — `[~][-]YYYY[/MM[/DD]] [<calendar shortcode>.<era shortcode>]` — and every field holding one parses through a single grammar into a single record. Print `text`, order on `sort`, do arithmetic on `canonicalYear`. A bare value sits on the canonical axis and carries `era: null`; one naming an era carries the qualifier and no `canonicalYear` or `sort` keys at all until the era is resolved against the corpus. The literal `unknown` parses to the same record with the year half empty, carrying a null `sort` and a null `canonicalYear`, so nothing can order it to one end of a list.
630
+ A date on a note is one authored string. `<year>.<day>[:HHMMSS]` names a calendar-neutral day; `VR(720/5/14)` names an era marker and a date in that era's calendar. A marker is resolved from calendar notes in the corpus and contributes a canonical year, day, and sort key. Print `text`, order on `sort`, and do arithmetic on `canonicalYear`. The literal `unknown` has no sort key or canonical year. Unmarked slash dates and the addressed era qualifier are also accepted by the parser; a caller supplies the corpus marker map and world year length when it needs a marker resolved.
627
631
 
628
632
  | Export | Signature | Returns | Use it when |
629
633
  | ----------------------- | ------------------------------- | -------------------------------------------------- | ------------------------------------------------------------- |
@@ -632,6 +636,15 @@ A date on a note is one authored string — `[~][-]YYYY[/MM[/DD]] [<calendar sho
632
636
  | `ERA_QUALIFIER_PATTERN` | `const ERA_QUALIFIER_PATTERN` | — | checking that a string is an era qualifier a date could carry |
633
637
  | `parseNoteDate` | `parseNoteDate(value, options)` | `{date, findings}` — `date` is `null` when refused | reading a note's authored date, with every finding it earned |
634
638
 
639
+ ### `engine.reckoningMarkers`
640
+
641
+ The corpus registry maps each declared marker to exactly one era and its calendar. An era start may use the canonical axis or a marker already resolvable from the corpus; duplicate markers and cycles are findings.
642
+
643
+ | Export | Signature | Returns | Use it when |
644
+ | ------------------------- | --------------------------------------------- | ---------------------------------- | ----------------------------------------------------------- |
645
+ | `resolveReckoningMarkers` | `resolveReckoningMarkers(index, daysPerYear)` | `{markers, findings}` | resolving authored era markers and their starts |
646
+ | `reckoningContext` | `reckoningContext(index)` | `{markers, daysPerYear, findings}` | sharing one resolved date context across checks on a corpus |
647
+
635
648
  ### `engine.calendarNotes`
636
649
 
637
650
  A calendar is a `lore` note with `subType: calendar`, declaring the months it keeps, the weekdays it names, the seasons it marks and the eras it counts years in. How long the year is, how the day divides and how the moon moves are facts about the **world**, written on its `place` note and its moon's, and no calendar may restate them — which is why the months summing to the world's year is the only arithmetic a calendar note is held to. One definition is emitted per calendar, in array shape: Foundry core prunes the keys outside its schema, and Calendaria converts the arrays to its own keyed collections.
@@ -653,6 +666,20 @@ A calendar is a `lore` note with `subType: calendar`, declaring the months it ke
653
666
  | `compileCalendars` | `compileCalendars(index, opts)` | `{calendars, invariants}` | building every calendar a package declares, or none where it states no year |
654
667
  | `calendariaEnvelope` | `calendariaEnvelope(definition, opts)` | `object` — the import envelope | wrapping a definition for a hand import through Calendaria's settings |
655
668
 
669
+ ### `engine.calendarArtifacts`
670
+
671
+ `emitCalendarArtifacts` reads the package's current content index and writes
672
+ calendar files under `build/calendars/`. Each calendar produces a definition
673
+ and an import envelope containing that same definition. The command line entry
674
+ point is `package-build calendars --calendaria-version <version>`; run
675
+ `content-build content-index` first. The generated import envelope carries a
676
+ fixed ISO `exportedAt` sentinel so repeated builds of unchanged source emit
677
+ identical bytes.
678
+
679
+ | Export | Signature | Returns | Use it when |
680
+ | ----------------------- | ---------------------------------------------------- | ------------------------------- | ---------------------------------------------------- |
681
+ | `emitCalendarArtifacts` | `emitCalendarArtifacts({config, calendariaVersion})` | `{directory, calendars, files}` | writing calendar files for a package's Foundry stage |
682
+
656
683
  ### `engine.beingAge`
657
684
 
658
685
  The middle state of a being's `age`: absent beside a dated `born`, it is computed against the package's declared present — the two dates compared, not the two years subtracted, so a birthday later in the year than the present counts one less. `applyComputedBeingAge` fills it in on the frontmatter object a pass already holds, never on the note; `checkBeingAge` is `being`'s `NOTE_VOCABULARY` check, raising a warning where an authored `age` disagrees with what a dated `born` computes to. An estimate's `~` is read the way a date's is: the authored string stays verbatim, and `ageYears` carries the magnitude beside it.
@@ -864,7 +891,7 @@ Publishing a content tree as a website. Compiling a content tree into compendium
864
891
  | `renderPages` | `function renderPages(pages, options)` | {{written: number, byKind: Record<string, number>, tableErrors: object[], wikiErrors: object[]}} | Renders and writes every page. |
865
892
  | `resolveSitePass` | `function resolveSitePass(name, options)` | {{beforeLinks?: Function}} The bundle. | Resolves `site.pass` to its bundle. |
866
893
  | `sitePageDecorator` | `sitePageDecorator(config, index)` | Function | Add the site's infobox and being metadata to a rendered page. |
867
- | `buildSite` | `buildSite({ config, sqlTables, locate })` | {{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], mapFindings: object[], manifests: object\|null}} | Builds a Hugo content tree from a content tree, and reports what it found. |
894
+ | `buildSite` | `buildSite({ config, sqlTables })` | {{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], mapFindings: object[], manifests: object\|null}} | Builds a Hugo content tree from a content tree, and reports what it found. |
868
895
 
869
896
  ### `engine.sitePreview`
870
897
 
package/docs/commands.md CHANGED
@@ -157,6 +157,50 @@ $ package-build assets
157
157
 
158
158
  [Configuration](configuration.md).
159
159
 
160
+ ### `package-build calendars`
161
+
162
+ **NAME**
163
+
164
+ Emit calendar definitions and Calendaria import files from the content index.
165
+
166
+ **SYNOPSIS**
167
+
168
+ ```
169
+ package-build calendars --calendaria-version <version>
170
+ ```
171
+
172
+ **DESCRIPTION**
173
+
174
+ Reads the package's current JSONL content index and writes
175
+ `build/calendars/<shortcode>.json` and
176
+ `build/calendars/<shortcode>.calendaria.json` for each calendar note. The
177
+ import file contains the same definition and a fixed `exportedAt` sentinel,
178
+ making both files reproducible. A package with no calendars gets an empty
179
+ `build/calendars/` directory. Run `content-build content-index` after changing
180
+ notes and before this command. Stage the generated directory with
181
+ `packageBuild.assets` when the Foundry package should ship it.
182
+
183
+ **OPTIONS**
184
+
185
+ `--calendaria-version` is required and identifies the Calendaria
186
+ import format targeted by the envelope. It has the form `major.minor.patch`.
187
+
188
+ **EXIT STATUS**
189
+
190
+ 1 if the index is missing or invalid, a calendar shortcode is invalid or
191
+ duplicated, a calendar has no world year, or the version is invalid. Otherwise 0.
192
+
193
+ **EXAMPLES**
194
+
195
+ ```
196
+ $ package-build calendars --calendaria-version 1.4.2
197
+ ✅ 5 calendars emitted (10 files).
198
+ ```
199
+
200
+ **SEE ALSO**
201
+
202
+ `content-build content-index`, [Getting started](getting-started.md).
203
+
160
204
  ### `package-build schema`
161
205
 
162
206
  **NAME**
@@ -1885,12 +1929,25 @@ index — this package's records and every declared dependency's fetched
1885
1929
  index — so a consumer's map draws its dependencies' places beside its own
1886
1930
  and no note is opened except to locate a finding. Everything lands under
1887
1931
  `build/map/`, always the `.dot` beside the `.svg`, so a drawing can be
1888
- re-rendered, diffed or read without the index. GraphViz draws: `dot` (or
1889
- `twopi`, `neato`) for the tree, `neato` for the others. It is a build-time
1890
- tool of this command alone; when the engine a run needs is not installed
1891
- the command says so, names what to install, and stops. A build that asks
1892
- for maps as one step of a larger job — a site build — writes the `.dot`
1893
- files, emits one warning and skips the renderings rather than failing.
1932
+ re-rendered, diffed or read without the index. The npm-installed Graphviz runtime draws with `dot` (or `twopi`, `neato`)
1933
+ for the tree and `neato` for the other drawings.
1934
+
1935
+ The containment tree draws every `world` note as a separate root. A map from a
1936
+ place follows that place's own relations, so another world in the package does
1937
+ not change its viewpoint.
1938
+
1939
+ Site builds put a linked SVG in each eligible place page. PDF builds put a
1940
+ vector map on its own page immediately after each selected place entry with
1941
+ relations. A configured Scene pack stages PNG backgrounds and emits gridless
1942
+ itinerary Scenes and an Adventure containing the journals their pins open.
1943
+ The installed SVG renderer also stages PNG backgrounds for authored regional SVG maps.
1944
+
1945
+ Battle and local map notes can carry a Foundry Scene export under `data.scene`.
1946
+ The pack compiler preserves its fields and binds pins marked `#anchor` to pages
1947
+ from the map note's Markdown. A `totm` map uses an image Address and canvas
1948
+ dimensions to make a gridless Scene.
1949
+ `data.fixup` resolves asset Addresses into selected fields of an exported Scene;
1950
+ a missing path or asset is a build error.
1894
1951
 
1895
1952
  The four drawings, any of which may be asked for in one run:
1896
1953
 
@@ -2019,8 +2076,7 @@ page can inline the SVG. Without it the names are plain text.
2019
2076
 
2020
2077
  1 when no drawing is asked for, when a `--from`, `--chart` or `--root`
2021
2078
  names no place, when `--horizon` is not a marker of the days scale, when
2022
- the GraphViz engine a drawing needs is not installed, when a
2023
- dependency's index cannot be read, when a note the index cannot record is
2079
+ the renderer fails, when a dependency's index cannot be read, when a note the index cannot record is
2024
2080
  met, on any error finding from `--tree` (a place with no parent, a parent
2025
2081
  that does not resolve, a cycle), or on any other thrown error. Otherwise 0
2026
2082
  — the warnings are reported and never fail the command.
@@ -2040,7 +2096,7 @@ $ content-build map --chart takheperurgn --horizon 90
2040
2096
  […] 394 place(s) → 1 drawing(s) under build/map
2041
2097
 
2042
2098
  $ content-build map --travel
2043
- […] GraphViz's `neato` is not installed, and `content-build map` draws with it; install GraphViz (`brew install graphviz` on macOS, `apt-get install graphviz` on Debian or Ubuntu) and run the command again
2099
+ […] 394 place(s) → 1 drawing(s) under build/map
2044
2100
  ```
2045
2101
 
2046
2102
  **SEE ALSO**
@@ -958,9 +958,8 @@ every place that states a border or a route or is named in one, lays it beside
958
958
  the page as `from-<shortcode>.svg`, and names the file in the page's front
959
959
  matter as `map`, which the theme inlines so every place name on it is a link
960
960
  to that place's page. A place with no relation carries no map and no key.
961
- GraphViz draws; when it is not installed the build says so once, as a
962
- warning, and writes every page as it would without maps — a site never fails
963
- for want of one. `false` draws nothing and asks nothing of GraphViz.
961
+ The Graphviz runtime installed with package-build draws the maps. `false`
962
+ draws nothing.
964
963
 
965
964
  ```yaml
966
965
  site:
@@ -1039,6 +1038,10 @@ is [`publish.site`](#publish), the same switch that gates the website —
1039
1038
  `content` builds one, `homepage` does not, so a package cannot end up with
1040
1039
  two switches that disagree about whether it publishes its content tree.
1041
1040
 
1041
+ Each selected place with a border or route receives a dedicated full-page
1042
+ vector itinerary after its entry. A selected authored regional SVG map also
1043
+ prints on its own page. The npm-installed Graphviz runtime draws itineraries.
1044
+
1042
1045
  ```yaml
1043
1046
  pdf:
1044
1047
  title: The Hârn Ensemble