@heroiclands/package-build 22.16.0 → 22.17.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 22.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ **Calendar dates**
8
+
9
+ - Calendar notes anchor their first year to a canonical day, count eras from calendar years, and include an era for dates before year one.
10
+ - Named calendar formats control how dates print; the standard format also controls how authored calendar dates are read.
11
+ - Calendars can name special days and divide the year into seasons, with those names available in formatted dates.
12
+
13
+ **Content dates**
14
+
15
+ - A date written as a year covers that whole year; a written day covers that day, and a clock time identifies a moment.
16
+ - Calendar starting days and moon phase references require a day so their dates cannot silently shift to the first day of a year.
17
+
3
18
  ## 22.16.0
4
19
 
5
20
  ### Minor Changes
@@ -540,7 +540,10 @@ function datefromCommand() {
540
540
  type: "string",
541
541
  describe: "Calendar Address or shortcode",
542
542
  })
543
- .positional("date", { type: "string", describe: "Day in that calendar" }),
543
+ .positional("date", {
544
+ type: "string",
545
+ describe: "Day written in that calendar's std format (or first named format)",
546
+ }),
544
547
  handler: handler((args) => {
545
548
  console.log(dateFromCalendar(args.calendar, args.date, calendarConversionContext()));
546
549
  }),
@@ -561,10 +564,19 @@ function datetoCommand() {
561
564
  .positional("canonical-date", {
562
565
  type: "string",
563
566
  describe: "Canonical <year>.<day>[:HHMMSS]",
567
+ })
568
+ .option("format", {
569
+ type: "string",
570
+ describe: "Named calendar format; defaults to std or the first declared format",
564
571
  }),
565
572
  handler: handler((args) => {
566
573
  console.log(
567
- dateToCalendar(args.calendar, args.canonicalDate, calendarConversionContext()),
574
+ dateToCalendar(
575
+ args.calendar,
576
+ args.canonicalDate,
577
+ calendarConversionContext(),
578
+ args.format,
579
+ ),
568
580
  );
569
581
  }),
570
582
  };
package/docs/api.md CHANGED
@@ -623,7 +623,7 @@ The closed registry of system ids, and the `none` that stands for no system at a
623
623
 
624
624
  ### `engine.calendars`
625
625
 
626
- 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.
626
+ The axis every date normalises onto, the arithmetic over a calendar's month list, and the moon. A calendar's `data.epoch` anchors its year 1, day 1 on that axis; its eras declare in-calendar start years. 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` uses a floored modulo on either side of the epoch.
627
627
 
628
628
  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.
629
629
 
@@ -647,29 +647,29 @@ The neutral day spelling is `<year>.<day>[:HHMMSS]`. The pure conversion functio
647
647
 
648
648
  ### `engine.noteDates`
649
649
 
650
- A date on a note is one authored string. `<year>.<day>[:HHMMSS]` names a calendar-neutral day; `datefrom vrcal 23 Taranis 326 VR` names a calendar, a named month, and an era. Either form may begin with `~` for an approximate date. A resolved calendar date carries a canonical year, day, span and sort key. Print `prose ?? text`, order on `sort`, and do arithmetic on `canonicalYear`. The literal `unknown` has no sort key or canonical year. `resolvedDateFields` supplies identical derived records to JSONL and generated page frontmatter.
651
-
652
- | Export | Signature | Returns | Use it when |
653
- | ----------------------- | ------------------------------------------------ | -------------------------------------------------- | -------------------------------------------------------------- |
654
- | `UNKNOWN_DATE` | `const UNKNOWN_DATE` | — | naming the literal that says a date is not recorded |
655
- | `NOTE_DATE_PATTERN` | `const NOTE_DATE_PATTERN` | — | matching the date grammar directly |
656
- | `DATEFROM_PATTERN` | `const DATEFROM_PATTERN` | — | recognizing a named calendar conversion in frontmatter |
657
- | `ERA_QUALIFIER_PATTERN` | `const ERA_QUALIFIER_PATTERN` | — | checking that a string is an era qualifier a date could carry |
658
- | `parseNoteDate` | `parseNoteDate(value, options)` | `{date, findings}` — `date` is `null` when refused | reading a note's authored date, with every finding it earned |
659
- | `eraCovering` | `eraCovering(date, eras, daysPerYear)` | one era or `null`; throws on overlap | selecting the era covering a resolved date's full precision |
660
- | `formatNoteDate` | `formatNoteDate(date, era, daysPerYear)` | `{era, year, month, day, text, prose}` or `null` | printing a resolved date in a chosen era or its authored form |
661
- | `formatDateInCalendar` | `formatDateInCalendar(date, reference, context)` | printable record or `null` | selecting a calendar's era and printing a resolved date in it |
662
- | `calendarEras` | `calendarEras(reference, context)` | ordered era records | resolving an addressed calendar or one of its eras |
663
- | `resolvedDateFields` | `resolvedDateFields(fm, context)` | map of normalized dates | deriving being and lore event date records for index and pages |
650
+ A date on a note is one authored scalar. `<year>[.<day>[:HHMMSS]]` names a calendar-neutral year, day, or second; `datefrom vrcal 23 Taranis 326 VR` names a calendar, a named month, and an era. Year and day values span their written interval; a clock time identifies a second. Either form may begin with `~` for uncertainty beyond that interval. The `approximate` field records that explicit marker, while `precision` and `seconds` describe the interval or time. A resolved date carries a canonical year, span, and sort key; a neutral year has no `canonicalDay`, while day-precision dates have one. Print `prose ?? text`, order on `sort`, and do arithmetic on `canonicalYear`. The literal `unknown` has no sort key or canonical year. `resolvedDateFields` supplies identical derived records to JSONL and generated page frontmatter.
651
+
652
+ | Export | Signature | Returns | Use it when |
653
+ | ----------------------- | ------------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------------- |
654
+ | `UNKNOWN_DATE` | `const UNKNOWN_DATE` | — | naming the literal that says a date is not recorded |
655
+ | `NOTE_DATE_PATTERN` | `const NOTE_DATE_PATTERN` | — | matching the date grammar directly |
656
+ | `DATEFROM_PATTERN` | `const DATEFROM_PATTERN` | — | recognizing a named calendar conversion in frontmatter |
657
+ | `ERA_QUALIFIER_PATTERN` | `const ERA_QUALIFIER_PATTERN` | — | checking that a string is an era qualifier a date could carry |
658
+ | `parseNoteDate` | `parseNoteDate(value, options)` | `{date, findings}` — `date` is `null` when refused | reading a note's authored date, with every finding it earned |
659
+ | `eraCovering` | `eraCovering(date, eras, daysPerYear)` | one era or `null`; throws on overlap | selecting the era covering a resolved date's full precision |
660
+ | `formatNoteDate` | `formatNoteDate(date, era, daysPerYear, formatName?)` | `{era, year, month, day, text, prose}` or `null` | printing a resolved date in a chosen era or its authored form |
661
+ | `formatDateInCalendar` | `formatDateInCalendar(date, reference, context, formatName?)` | printable record or `null` | selecting a calendar's era and printing a resolved date in it |
662
+ | `calendarEras` | `calendarEras(reference, context)` | ordered era records | resolving an addressed calendar or one of its eras |
663
+ | `resolvedDateFields` | `resolvedDateFields(fm, context)` | map of normalized dates | deriving being and lore event date records for index and pages |
664
664
 
665
665
  ### `engine.dateConversion`
666
666
 
667
- The exact-day conversion functions use the corpus reckoning context. A calendar with several eras requires an era in the named date. They reject dates outside the selected calendar and gaps between its eras. Approximate input keeps its `~` prefix in the result.
667
+ The exact-day conversion functions use the corpus reckoning context. `dateFromCalendar` reads the `std` pattern, or the first named pattern. An era-relative year needs its era label. They reject dates outside the selected era; approximate input keeps its `~` prefix.
668
668
 
669
- | Export | Signature | Returns | Use it when |
670
- | ------------------ | --------------------------------------------------- | --------------------- | --------------------------------------------- |
671
- | `dateFromCalendar` | `dateFromCalendar(reference, value, context)` | canonical date string | converting one calendar day to the world axis |
672
- | `dateToCalendar` | `dateToCalendar(reference, canonicalDate, context)` | calendar date string | expressing one canonical day in a calendar |
669
+ | Export | Signature | Returns | Use it when |
670
+ | ------------------ | ---------------------------------------------------------------- | --------------------- | --------------------------------------------- |
671
+ | `dateFromCalendar` | `dateFromCalendar(reference, value, context)` | canonical date string | converting one calendar day to the world axis |
672
+ | `dateToCalendar` | `dateToCalendar(reference, canonicalDate, context, formatName?)` | calendar date string | expressing one canonical day in a calendar |
673
673
 
674
674
  ### `engine.calendarChoice`
675
675
 
@@ -700,7 +700,7 @@ invokes the check during frontmatter lint.
700
700
 
701
701
  ### `engine.reckoningMarkers`
702
702
 
703
- The corpus registry maps every declared era to its calendar and each marker to exactly one era. Era starts may use the canonical axis or another declared era. Within one calendar, starts increase; the next start bounds the preceding era unless an explicit end leaves a gap. The first era also covers dates before its start and counts them with negative years. Later eras accept only forward year counts. Unknown eras, duplicate markers, invalid bounds, overlapping eras and dependency cycles are findings. Short qualifiers are ambiguous when they name eras in several packages; a full address selects one. This registry governs note dates; calendar definitions emitted for Foundry retain their consumer format.
703
+ The corpus registry anchors each calendar's year 1, day 1 to its canonical `data.epoch` and maps every declared era to that calendar. Exactly one `start: null` era covers earlier years and counts backwards; exactly one `start: 1` era begins the forward count. Other starts are distinct positive calendar years. Numeric order determines bounds, irrespective of array order. Duplicate starts and markers, missing required eras, and invalid epochs are findings. Short qualifiers are ambiguous when they name eras in several packages; a full Address selects one.
704
704
 
705
705
  | Export | Signature | Returns | Use it when |
706
706
  | ------------------------- | --------------------------------------------- | ---------------------------------------- | ----------------------------------------------------------- |
@@ -720,7 +720,7 @@ A calendar is a `lore` note with `subType: calendar`, declaring the months it ke
720
720
  | `CALENDAR_FORMAT_TOKENS` | `const CALENDAR_FORMAT_TOKENS` | — | checking bare date tokens in a format string |
721
721
  | `INVARIANT_FIELDS` | `const INVARIANT_FIELDS` | — | reading the closed list of what a body may declare about itself |
722
722
  | `checkCalendarNote` | `checkCalendarNote(note, opts)` | `object[]` — findings | holding a `lore` note to the family and to the year the world keeps |
723
- | `checkCalendarDateFormats` | `checkCalendarDateFormats(note)` | `object[]` — findings | checking display slots and escaped literal words |
723
+ | `checkCalendarDateFormats` | `checkCalendarDateFormats(note)` | `object[]` — findings | checking named patterns and the readable default |
724
724
  | `checkWorldFacts` | `checkWorldFacts(note, opts)` | `object[]` — findings | holding a `place` note's world facts to one body, one year and one moon |
725
725
  | `worldInvariants` | `worldInvariants(index)` | `{year, present, moon, body, moonName, …}` | reading the year, the present and the moon a package's own notes state |
726
726
  | `quarterDays` | `quarterDays(daysPerYear)` | `number[]` — four days of the year | asking which days the year turns on |
@@ -7,27 +7,79 @@ subType: howto
7
7
 
8
8
  # Dates, calendars, and eras
9
9
 
10
- A canonical date names a day without assuming how a culture divides a year. Write `<year>.<day>[:HHMMSS]`: `720.136` is day 136 of year 720, and `720.136:143005` adds a 24-hour time. A negative year is valid. The day must fit the world's declared year length. The dot separates year from **day of year**, not month from day.
10
+ A canonical date uses the world's year and day count without choosing a culture's calendar. Write `<year>[.<day>[:HHMMSS]]`: `720` covers year 720, `720.136` covers its day 136, and `720.136:143005` identifies 14:30:05 on that day. The dot separates the year from the **day of year**, not a month. Negative years are valid, and the day must fit the world's declared year length. Prefix a date with `~` when its uncertainty extends outside the stated year, day, or second. `unknown` records an occurrence whose date is not known, in fields that permit it.
11
11
 
12
- Authors can instead write a named calendar date in frontmatter:
12
+ ## Define a calendar
13
+
14
+ A `lore` note with `subType: calendar` names months, weekdays, seasons, named days, eras, and date formats. Its `data.epoch` is the **canonical day that equals calendar year 1, day 1**. For a calendar beginning on canonical day `1.91`, write `epoch: 1.91`: calendar year 1 still starts at its own day 1. A calendar does not state the length of the world year: its months must sum to `data.year.days` on the world's `place` note.
13
15
 
14
16
  ```yaml
17
+ shortcode: commoncal
18
+ name: { full: The Common Calendar }
19
+ type: lore
20
+ subType: calendar
15
21
  data:
16
- born: datefrom vrcal 23 Taranis 326 VR
17
- died: ~326.114
22
+ epoch: 1.1
23
+ months:
24
+ - { name: Floralis, abbreviation: Flor, days: 30 }
25
+ - { name: Lusenar, abbreviation: Luse, days: 31 }
26
+ - { name: Murkas, abbreviation: Murk, days: 30 }
27
+ - { name: Taranis, abbreviation: Tara, days: 31 }
28
+ - { name: Vulcar, abbreviation: Vulc, days: 30 }
29
+ - { name: Menaris, abbreviation: Mena, days: 31 }
30
+ - { name: Venuris, abbreviation: Venu, days: 30 }
31
+ - { name: Karnavar, abbreviation: Karn, days: 30 }
32
+ - { name: Morveth, abbreviation: Morv, days: 31 }
33
+ - { name: Thanaris, abbreviation: Than, days: 30 }
34
+ - { name: Aetheris, abbreviation: Aeth, days: 31 }
35
+ - { name: Janar, abbreviation: Jana, days: 30 }
36
+ weekdays:
37
+ - { name: Newday, abbreviation: New }
38
+ - { name: Tillday, abbreviation: Til }
39
+ - { name: Growday, abbreviation: Gro }
40
+ - { name: Harvestday, abbreviation: Hrv }
41
+ - { name: Reapday, abbreviation: Rep }
42
+ - { name: Slowday, abbreviation: Slo }
43
+ - { name: Setday, abbreviation: Set }
44
+ seasons:
45
+ - { name: Spring, abbreviation: Spr, start: 1 }
46
+ - { name: Summer, abbreviation: Sum, start: 92 }
47
+ - { name: Fall, abbreviation: Fal, start: 183 }
48
+ - { name: Winter, abbreviation: Win, start: 274 }
49
+ namedDays:
50
+ - { name: New Years Day, abbreviation: NY, day: 1 }
51
+ formats:
52
+ std: "MM/DD/Y GGG"
53
+ long: "D MMMM Y GGG"
54
+ eras:
55
+ - { shortcode: bvr, name: Before Vylarian Reckoning, abbreviation: BVR, start: null }
56
+ - { shortcode: vr, name: Vylarian Reckoning, abbreviation: VR, start: 1 }
18
57
  ```
19
58
 
20
- `datefrom` takes a calendar shortcode or Address, then the date in that calendar. The calendar defines month names and eras. A date can have day, month, or year precision. Prefix either spelling with `~` for an approximate date. `unknown` means an occurrence is known but its date is not recorded, in fields that accept it. These are the frontmatter date forms; do not put a Handlebars expression there.
59
+ The `weekdays` array gives the names in order, starting at index 0. Weeks continue across month and year boundaries. Leave `weekdays` out when the calendar has no week. `seasons[].start` is the one-based day of year on which a season begins. Starts increase in array order. A season continues through the day before the next, and the last wraps through the beginning of the next year when the first season starts after day 1. `namedDays[].day` is also a one-based day of year. Its name appears through `[namedDay]`; `[namedDayAbbr]` prints the abbreviation.
60
+
61
+ Every calendar has exactly one era with `start: null` and one with `start: 1`. The null era covers the years before calendar year 1 and counts them backwards: under `Y GGG`, package-build prints canonical `-50.2` as `50 BVR` when the epoch is `1.1`. This null-start era is an authoring extension and is omitted from the Calendaria definition; Foundry's calendar represents current time. Each other `start` is a distinct, positive **calendar year**, never a day-precise date: `start: 701` begins on day 1 of calendar year 701. `start: 1` begins on the canonical day given by `epoch`, even when that day is `1.91` or another date. Eras end where the next numeric start begins. Array order does not determine chronology. The optional `marker` identifies an era in authored dates and must be unique across the corpus.
62
+
63
+ `formats` contains any number of named Calendaria patterns. `std` is the default for `dateto`, `datefrom`, and `dateformat`; if absent, the first named pattern is the default. The default pattern must contain enough information to read a day back: a complete year (`Y`, `YYYY`, or `[yearInEra]`), a day of year (`DDD`) or month and day, and an era label when using `[yearInEra]`. `Y` means the calendar year in Calendaria; `[yearInEra]` means the year counted from the era's start. In package-build's null-start era, `Y`, `YY`, and `YYYY` print the positive historical year. Other named patterns can use display-only Calendaria tokens. Use `package-build dateto <calendar> <canonical-date> --format <name>` or `{{dateformat "commoncal" data.born "long"}}` to select another output format. The [format token reference](../reference/format-details.md#calendar-format-tokens) lists every Calendaria token and the input constraints.
64
+
65
+ ## Write and convert dates
21
66
 
22
- A calendar defines the order and length of months. An era defines where a year count begins. One calendar can have several eras; a multi-era date names its era. Era starts increase on the canonical timeline, with gaps allowed. Dates before the first era use a negative year in that first era; later eras count forward from their own year 1. There is no year zero. A date in a gap has no named era in that calendar.
67
+ A frontmatter date can use the canonical form or `datefrom <calendar> <date in the default format>`. The calendar may be a shortcode or Address. A named date keeps the precision it states: with the sample `std` pattern, `326 VR` covers a year, `04/326 VR` covers Taranis, and `04/23/326 VR` covers its twenty-third day. A date with an era-relative year names its era.
68
+
69
+ ```yaml
70
+ data:
71
+ born: datefrom commoncal 04/23/326 VR
72
+ died: ~326.114
73
+ ```
23
74
 
24
- Use the CLI to inspect a conversion:
75
+ The conversion commands take the same forms:
25
76
 
26
77
  ```bash
27
- package-build datefrom vrcal '23 Taranis 326 VR'
28
- package-build dateto vrcal 326.114
78
+ package-build datefrom commoncal '04/23/326 VR'
79
+ package-build dateto commoncal 326.114
80
+ package-build dateto commoncal 326.114 --format long
29
81
  ```
30
82
 
31
- `datefrom` prints the canonical value; `dateto` prints a named date in the era covering it. The [detailed calendar rules](../reference/format-details.md#dates-and-calendars) explain precision, era markers, bounds, and resolution errors.
83
+ The CLI `datefrom` command requires a day and reads the default (`std`, or first) pattern. `dateto` prints the era covering the canonical day. A clock time makes a date exact; a year or day without one spans the stated interval. `data.epoch` and `data.moon.newOn` require a day. The [detailed date rules](../reference/format-details.md#dates-and-calendars) describe precision and bounds.
32
84
 
33
- In prose, `{{dateformat "vrcal" data.born}}` renders a date in the chosen calendar. `{{dateformat data.calendar data.born}}` uses the note's own calendar Address. A being or place can set `data.calendar`; a being's infobox displays its dates in that calendar, and a place's infobox displays its `present` there.
85
+ In prose, `{{dateformat "commoncal" data.born}}` prints a date in the chosen calendar. `{{dateformat data.calendar data.born}}` uses the note's calendar Address. A third argument chooses another named pattern. A being or place can set `data.calendar`; its infobox uses that calendar for dates.
@@ -134,4 +134,4 @@ The index contains {{digits (sql "SELECT COUNT(*) FROM entries")}} entries.
134
134
  {{and (gt 3 5) (lt 4 2)}}
135
135
  ```
136
136
 
137
- Use `{{dateformat data.calendar data.born}}` when the note supplies a calendar Address. SQL fences produce tables; the `sql` helper returns one value for an inline expression. Wrap a numeric result in `words` for running prose or `digits` for a grouped numeral. See [date rules](dates-and-calendars.md) and [SQL details](../reference/format-details.md#content-tables) for query options.
137
+ Use `{{dateformat data.calendar data.born}}` when the note supplies a calendar Address. A third argument selects a named output pattern, as in `{{dateformat data.calendar data.born "long"}}`. SQL fences produce tables; the `sql` helper returns one value for an inline expression. Wrap a numeric result in `words` for running prose or `digits` for a grouped numeral. See [date rules](dates-and-calendars.md) and [SQL details](../reference/format-details.md#content-tables) for query options.
package/docs/commands.md CHANGED
@@ -382,9 +382,10 @@ package-build datefrom <calendar> <date>
382
382
 
383
383
  Reads the current content tree, resolves its world year and calendar eras, and
384
384
  prints `<year>.<day>[:HHMMSS]` to standard output. `<calendar>` is a calendar
385
- shortcode, full Address, or addressed era. The date uses the calendar's month
386
- name and era label, for example `23 Taranis 326 VR`. A date in a calendar with
387
- multiple eras must identify its era. A date must specify a day;
385
+ shortcode, full Address, or addressed era. The date follows the calendar's
386
+ `data.formats.std` pattern, or its first named pattern when `std` is absent.
387
+ For example, `MM/DD/Y GGG` reads `04/23/326 VR`. An era-relative year written
388
+ with `[yearInEra]` must identify its era. A date must specify a day;
388
389
  year-only and month-only dates do not identify one canonical day.
389
390
  Prefix the date with `~` to preserve an approximate value.
390
391
 
@@ -417,19 +418,20 @@ Express a canonical day in an authored calendar.
417
418
  **SYNOPSIS**
418
419
 
419
420
  ```
420
- package-build dateto <calendar> <canonical-date>
421
+ package-build dateto <calendar> <canonical-date> [--format <name>]
421
422
  ```
422
423
 
423
424
  **DESCRIPTION**
424
425
 
425
426
  Reads the current content tree, selects the era covering the canonical day, and
426
- prints the date with its named month and era label. The input is
427
+ prints the date using `data.formats.std`, or the first named pattern if `std`
428
+ is absent. The input is
427
429
  `<year>.<day>[:HHMMSS]`, optionally prefixed with `~`. A date in a gap between
428
430
  eras has no conversion.
429
431
 
430
432
  **OPTIONS**
431
433
 
432
- None.
434
+ `--format` selects another named pattern from the calendar note; pass its name as the value.
433
435
 
434
436
  **EXIT STATUS**
435
437