@heroiclands/package-build 22.15.1 → 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 +110 -0
- package/README.md +39 -678
- package/bin/{content-build.mjs → content-commands.mjs} +197 -118
- package/bin/package-build.mjs +315 -42
- package/bin/report.mjs +1 -1
- package/ci/ci-docker.mjs +6 -3
- package/ci/ci-steps.mjs +6 -4
- package/ci/content-format-reference.mjs +106 -0
- package/config.mjs +22 -2
- package/content-config.mjs +74 -134
- package/docs/api.md +296 -236
- package/docs/authoring/assets.md +31 -0
- package/docs/authoring/dates-and-calendars.md +85 -0
- package/docs/authoring/first-note.md +48 -0
- package/docs/authoring/frontmatter.md +40 -0
- package/docs/authoring/links-and-markup.md +137 -0
- package/docs/commands.md +633 -162
- package/docs/configuration.md +176 -140
- package/docs/content-format.md +50 -4229
- package/docs/design.md +32 -0
- package/docs/diagnostics.md +29 -22
- package/docs/getting-started.md +21 -877
- package/docs/guides/book.md +153 -0
- package/docs/guides/e2e.md +143 -0
- package/docs/guides/icons.md +126 -0
- package/docs/guides/localization.md +120 -0
- package/docs/guides/module-project.md +915 -0
- package/docs/guides/packs.md +167 -0
- package/docs/guides/reference-pages.md +148 -0
- package/docs/guides/release-and-deploy.md +114 -0
- package/docs/guides/site.md +152 -0
- package/docs/guides/system-project.md +45 -0
- package/docs/index.md +16 -0
- package/docs/project-setup.md +54 -35
- package/docs/reference/format-details.md +4362 -0
- package/docs/reference/note-types.md +532 -0
- package/engine/address-render.mjs +26 -0
- package/engine/address.mjs +3 -20
- package/engine/anchors.mjs +15 -6
- package/engine/asset-bindings.mjs +6 -2
- package/engine/base-compiler.mjs +47 -11
- package/engine/being-measurements.mjs +70 -0
- package/engine/calendar-artifacts.mjs +1 -1
- package/engine/calendar-choice.mjs +32 -0
- package/engine/calendar-format.mjs +441 -0
- package/engine/calendar-human.mjs +133 -0
- package/engine/calendar-notes.mjs +307 -88
- package/engine/calendars.mjs +8 -1
- package/engine/changelog-lint.mjs +2 -2
- package/engine/code-fences.mjs +23 -97
- package/engine/compile-corpus.mjs +6 -7
- package/engine/content-admonitions.mjs +114 -0
- package/engine/content-captions.mjs +181 -0
- package/engine/content-embeds.mjs +2 -2
- package/engine/content-footnotes.mjs +34 -0
- package/engine/content-format-check.mjs +2 -2
- package/engine/content-format.mjs +127 -73
- package/engine/content-format.yaml +746 -0
- package/engine/content-html.mjs +1 -1
- package/engine/content-icons.mjs +45 -92
- package/engine/content-images.mjs +22 -74
- package/engine/content-index.mjs +12 -3
- package/engine/content-links.mjs +21 -26
- package/engine/content-lint.mjs +1 -5
- package/engine/content-secrets.mjs +3 -1
- package/engine/content-tables.mjs +47 -1257
- package/engine/culture-choice.mjs +32 -0
- package/engine/date-conversion.mjs +47 -0
- package/engine/declaration-check.mjs +87 -0
- package/engine/docs-checks.mjs +140 -0
- package/engine/documentation-links.mjs +31 -0
- package/engine/exported-scene.mjs +1 -1
- package/engine/extension-attributes.mjs +74 -0
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +4 -0
- package/engine/foreign-catalog.mjs +5 -5
- package/engine/format-generated.mjs +15 -0
- package/engine/foundry-entries.mjs +3 -1
- package/engine/frontmatter-lint.mjs +5 -1
- package/engine/frontmatter.mjs +10 -0
- package/engine/generate.mjs +2 -2
- package/engine/helpers.mjs +71 -117
- package/engine/homepage.mjs +2 -3
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +18 -3
- package/engine/infobox-registry.mjs +3 -1
- package/engine/infobox.mjs +38 -20
- package/engine/item-compiler.mjs +10 -8
- package/engine/item-docs.mjs +7 -6
- package/engine/journals.mjs +51 -10
- package/engine/macros.mjs +1 -1
- package/engine/map-build.mjs +1 -1
- package/engine/map-dot.mjs +1 -1
- package/engine/markdown-expressions.mjs +118 -0
- package/engine/metadata-index.mjs +7 -3
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-dates.mjs +314 -64
- package/engine/note-package.mjs +5 -25
- package/engine/note-state.mjs +6 -2
- package/engine/note-vocabulary.mjs +74 -19
- package/engine/number-words.mjs +75 -0
- package/engine/office-holders.mjs +188 -0
- package/engine/pack-config.mjs +2 -15
- package/engine/pack-router.mjs +24 -5
- package/engine/packages.mjs +3 -2
- package/engine/pathnames.mjs +21 -24
- package/engine/pdf-build.mjs +217 -55
- package/engine/pdf-fonts.mjs +2 -2
- package/engine/pdf-images.mjs +82 -0
- package/engine/pdf-render.mjs +174 -22
- package/engine/populations.mjs +3 -2
- package/engine/project-init.mjs +499 -0
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +7 -6
- package/engine/readability-lint.mjs +86 -0
- package/engine/reckoning-markers.mjs +176 -166
- package/engine/scenes.mjs +5 -2
- package/engine/schema-check.mjs +3 -3
- package/engine/site-build.mjs +87 -37
- package/engine/site-config.mjs +8 -8
- package/engine/site-index.mjs +4 -0
- package/engine/site-maps.mjs +1 -1
- package/engine/site-preview.mjs +19 -8
- package/engine/social-ties.mjs +5 -1
- package/engine/sql-tables.mjs +92 -22
- package/engine/web-wikilinks.mjs +15 -31
- package/engine/wikilink-syntax.mjs +4 -1
- package/engine/wikilinks.mjs +12 -14
- package/hm3/item-fields.mjs +1 -1
- package/hm3/items.mjs +12 -45
- package/index.mjs +2 -14
- package/manifest.mjs +31 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +32 -11
- package/prettier-config.mjs +1 -1
- package/sohl/being-info.mjs +3 -145
- package/sohl/index.mjs +1 -1
- package/sohl/infobox.mjs +2 -1
- package/sohl/item-builders.mjs +3 -27
- package/sohl/item-fields.mjs +61 -22
- package/sohl/kb-passes.mjs +2 -2
- package/sohl/note-schemas.mjs +0 -8
- package/stage.mjs +19 -0
- package/types/config.d.mts +12 -3
- package/types/content-config.d.mts +15 -81
- package/types/engine/address-render.d.mts +13 -0
- package/types/engine/address.d.mts +2 -11
- package/types/engine/anchors.d.mts +3 -4
- package/types/engine/asset-bindings.d.mts +9 -2
- package/types/engine/base-compiler.d.mts +1 -2
- package/types/engine/being-measurements.d.mts +16 -0
- package/types/engine/calendar-choice.d.mts +14 -0
- package/types/engine/calendar-format.d.mts +33 -0
- package/types/engine/calendar-human.d.mts +4 -0
- package/types/engine/calendar-notes.d.mts +5 -3
- package/types/engine/calendars.d.mts +5 -3
- package/types/engine/code-fences.d.mts +6 -72
- package/types/engine/compile-corpus.d.mts +1 -3
- package/types/engine/content-admonitions.d.mts +24 -0
- package/types/engine/content-captions.d.mts +45 -0
- package/types/engine/content-footnotes.d.mts +10 -0
- package/types/engine/content-format-check.d.mts +17 -4
- package/types/engine/content-format.d.mts +24 -11
- package/types/engine/content-icons.d.mts +14 -23
- package/types/engine/content-images.d.mts +2 -2
- package/types/engine/content-index.d.mts +9 -2
- package/types/engine/content-links.d.mts +3 -5
- package/types/engine/content-tables.d.mts +11 -140
- package/types/engine/culture-choice.d.mts +14 -0
- package/types/engine/date-conversion.d.mts +4 -0
- package/types/engine/declaration-check.d.mts +24 -0
- package/types/engine/docs-checks.d.mts +28 -0
- package/types/engine/documentation-links.d.mts +7 -0
- package/types/engine/exported-scene.d.mts +6 -2
- package/types/engine/extension-attributes.d.mts +14 -0
- package/types/engine/field-spec.d.mts +9 -0
- package/types/engine/format-generated.d.mts +7 -0
- package/types/engine/frontmatter.d.mts +14 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +37 -92
- package/types/engine/homepage.d.mts +2 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +3 -1
- package/types/engine/infobox.d.mts +17 -6
- package/types/engine/item-compiler.d.mts +4 -4
- package/types/engine/item-docs.d.mts +4 -3
- package/types/engine/journals.d.mts +10 -7
- package/types/engine/map-graphviz.d.mts +1 -1
- package/types/engine/markdown-expressions.d.mts +17 -0
- package/types/engine/note-dates.d.mts +47 -1
- package/types/engine/note-package.d.mts +3 -19
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/note-vocabulary.d.mts +9 -0
- package/types/engine/number-words.d.mts +4 -0
- package/types/engine/office-holders.d.mts +14 -0
- package/types/engine/pack-config.d.mts +1 -1
- package/types/engine/packages.d.mts +3 -2
- package/types/engine/pdf-images.d.mts +38 -0
- package/types/engine/populations.d.mts +2 -2
- package/types/engine/project-init.d.mts +17 -0
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +3 -2
- package/types/engine/readability-lint.d.mts +22 -0
- package/types/engine/reckoning-markers.d.mts +13 -26
- package/types/engine/schema-check.d.mts +3 -3
- package/types/engine/site-build.d.mts +38 -28
- package/types/engine/site-config.d.mts +2 -2
- package/types/engine/site-index.d.mts +4 -0
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/social-ties.d.mts +8 -2
- package/types/engine/sql-tables.d.mts +13 -2
- package/types/engine/web-wikilinks.d.mts +2 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/hm3/items.d.mts +5 -6
- package/types/labels.d.mts +40 -0
- package/types/manifest.d.mts +3 -3
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/being-info.d.mts +3 -43
- package/types/sohl/index.d.mts +1 -1
- package/types/sohl/infobox.d.mts +5 -2
- package/types/sohl/item-fields.d.mts +4 -13
- package/types/stage.d.mts +9 -0
- package/CONTENT.md +0 -3239
|
@@ -1,198 +1,208 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
-
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
-
*
|
|
5
|
-
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
-
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
-
*
|
|
8
|
-
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
9
|
-
*/
|
|
1
|
+
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
10
2
|
|
|
11
3
|
import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
12
|
-
import {
|
|
13
|
-
|
|
4
|
+
import {
|
|
5
|
+
canonicalDateFromOffset,
|
|
6
|
+
canonicalDayOffset,
|
|
7
|
+
canonicalYear,
|
|
8
|
+
parseCanonicalDate,
|
|
9
|
+
} from "./calendars.mjs";
|
|
10
|
+
import { renderAddress } from "./address-render.mjs";
|
|
14
11
|
|
|
15
|
-
/**
|
|
16
|
-
* Resolve every declared reckoning marker against its calendar and era start.
|
|
17
|
-
* The marker names one era, while the calendar supplies the month layout.
|
|
18
|
-
*
|
|
19
|
-
* @param {{notes?: readonly object[]}} index - The corpus link index.
|
|
20
|
-
* @param {number} daysPerYear - The world's year length.
|
|
21
|
-
* @returns {{markers: Map<string, object>, findings: object[]}} Resolved markers and errors.
|
|
22
|
-
*/
|
|
12
|
+
/** Resolve calendar eras on the canonical day axis. */
|
|
23
13
|
export function resolveReckoningMarkers(index, daysPerYear) {
|
|
24
|
-
const
|
|
14
|
+
const eras = new Map();
|
|
25
15
|
const markers = new Map();
|
|
26
16
|
const findings = [];
|
|
17
|
+
const qualifiers = new Map();
|
|
18
|
+
const markerOwners = new Map();
|
|
19
|
+
const calendarRows = [];
|
|
20
|
+
|
|
21
|
+
const location = (note, position, key) => ({
|
|
22
|
+
...(note.file === undefined ? {} : { file: note.file }),
|
|
23
|
+
...(note.raw === undefined ?
|
|
24
|
+
{}
|
|
25
|
+
: positionOfFrontmatterPath(note.raw, ["data", "eras", position, key])),
|
|
26
|
+
});
|
|
27
|
+
const error = (note, position, key, message) =>
|
|
28
|
+
findings.push({ ...location(note, position, key), severity: "error", message });
|
|
27
29
|
|
|
28
30
|
for (const note of index?.notes ?? []) {
|
|
29
31
|
const fm = note?.fm ?? note?.frontmatter ?? note;
|
|
30
32
|
if (fm?.type !== "lore" || fm?.subType !== "calendar") continue;
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const data = fm.data ?? {};
|
|
34
|
+
const rows = data.eras;
|
|
35
|
+
if (!Array.isArray(rows)) continue;
|
|
36
|
+
if (!Number.isSafeInteger(daysPerYear) || daysPerYear < 1) continue;
|
|
37
|
+
const epochText = String(data.epoch ?? "");
|
|
38
|
+
const epoch = parseCanonicalDate(epochText, daysPerYear);
|
|
39
|
+
if (!epoch || epoch.year === 0 || epoch.seconds !== undefined) {
|
|
40
|
+
findings.push({
|
|
37
41
|
...(note.file === undefined ? {} : { file: note.file }),
|
|
38
42
|
...(note.raw === undefined ?
|
|
39
43
|
{}
|
|
40
|
-
: positionOfFrontmatterPath(note.raw, ["data", "
|
|
41
|
-
};
|
|
42
|
-
if (typeof marker !== "string" || !/^[A-Z][A-Z0-9]*$/.test(marker)) {
|
|
43
|
-
findings.push({
|
|
44
|
-
...at,
|
|
45
|
-
severity: "error",
|
|
46
|
-
message:
|
|
47
|
-
"an era marker is uppercase letters and digits, starting with a letter",
|
|
48
|
-
});
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (declared.has(marker)) {
|
|
52
|
-
findings.push({
|
|
53
|
-
...at,
|
|
54
|
-
severity: "error",
|
|
55
|
-
message: `reckoning marker ${marker} is declared more than once`,
|
|
56
|
-
});
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
declared.set(marker, {
|
|
60
|
-
marker,
|
|
61
|
-
era: `${fm.shortcode}.${row.shortcode}`,
|
|
62
|
-
calendar: fm.data,
|
|
63
|
-
start: row.start,
|
|
64
|
-
end: row.end,
|
|
65
|
-
note,
|
|
66
|
-
position,
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const visiting = new Set();
|
|
72
|
-
const failed = new Set();
|
|
73
|
-
function resolve(marker) {
|
|
74
|
-
if (markers.has(marker)) return markers.get(marker);
|
|
75
|
-
if (failed.has(marker)) return null;
|
|
76
|
-
const row = declared.get(marker);
|
|
77
|
-
if (!row) return null;
|
|
78
|
-
if (visiting.has(marker)) {
|
|
79
|
-
findings.push({
|
|
80
|
-
...(row.note.file === undefined ? {} : { file: row.note.file }),
|
|
81
|
-
severity: "error",
|
|
82
|
-
message: `reckoning marker cycle includes ${[...visiting, marker].join(" → ")}`,
|
|
83
|
-
});
|
|
84
|
-
failed.add(marker);
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
visiting.add(marker);
|
|
88
|
-
if (row.start === undefined || row.start === null) {
|
|
89
|
-
findings.push({
|
|
90
|
-
...(row.note.file === undefined ? {} : { file: row.note.file }),
|
|
44
|
+
: positionOfFrontmatterPath(note.raw, ["data", "epoch"])),
|
|
91
45
|
severity: "error",
|
|
92
|
-
message:
|
|
46
|
+
message:
|
|
47
|
+
"data.epoch must be a canonical <year>.<day> anchoring calendar year 1, day 1",
|
|
93
48
|
});
|
|
94
|
-
|
|
49
|
+
continue;
|
|
95
50
|
}
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
51
|
+
const epochOffset = canonicalDayOffset(
|
|
52
|
+
canonicalYear(epoch.year),
|
|
53
|
+
epoch.day,
|
|
54
|
+
1,
|
|
55
|
+
daysPerYear,
|
|
56
|
+
);
|
|
57
|
+
const pkg = fm.package ?? note.package ?? index?.contentPackage;
|
|
58
|
+
const declared = [];
|
|
59
|
+
for (const [position, era] of rows.entries()) {
|
|
60
|
+
if (!era || typeof era.shortcode !== "string" || !era.shortcode || !fm.shortcode)
|
|
61
|
+
continue;
|
|
62
|
+
const short = `${fm.shortcode}.${era.shortcode}`;
|
|
63
|
+
const key =
|
|
64
|
+
pkg ?
|
|
65
|
+
renderAddress({ package: pkg, system: "note", type: "lore", shortcode: short })
|
|
66
|
+
: short;
|
|
67
|
+
const row = {
|
|
68
|
+
key,
|
|
69
|
+
era: short,
|
|
70
|
+
calendar: data,
|
|
71
|
+
calendarShortcode: fm.shortcode,
|
|
72
|
+
note,
|
|
73
|
+
position,
|
|
74
|
+
start: era.start,
|
|
75
|
+
name: era.name,
|
|
76
|
+
abbreviation: era.abbreviation,
|
|
77
|
+
marker: era.marker,
|
|
78
|
+
label: era.label,
|
|
79
|
+
};
|
|
80
|
+
declared.push(row);
|
|
81
|
+
const aliases = [short, `lore-${short}`, `note-lore-${short}`];
|
|
82
|
+
for (const alias of aliases) {
|
|
83
|
+
if (qualifiers.has(alias) && qualifiers.get(alias) !== key)
|
|
84
|
+
qualifiers.set(alias, null);
|
|
85
|
+
else qualifiers.set(alias, key);
|
|
130
86
|
}
|
|
87
|
+
if (era.marker !== undefined) {
|
|
88
|
+
if (typeof era.marker !== "string" || !/^[A-Z][A-Z0-9]*$/.test(era.marker))
|
|
89
|
+
error(
|
|
90
|
+
note,
|
|
91
|
+
position,
|
|
92
|
+
"marker",
|
|
93
|
+
"an era marker needs uppercase letters and digits",
|
|
94
|
+
);
|
|
95
|
+
else if (markerOwners.has(era.marker))
|
|
96
|
+
error(
|
|
97
|
+
note,
|
|
98
|
+
position,
|
|
99
|
+
"marker",
|
|
100
|
+
`reckoning marker ${era.marker} is declared more than once`,
|
|
101
|
+
);
|
|
102
|
+
else markerOwners.set(era.marker, key);
|
|
103
|
+
}
|
|
104
|
+
if (era.end !== undefined)
|
|
105
|
+
error(
|
|
106
|
+
note,
|
|
107
|
+
position,
|
|
108
|
+
"end",
|
|
109
|
+
"an era ends when the next calendar year era begins; omit end",
|
|
110
|
+
);
|
|
131
111
|
}
|
|
132
|
-
|
|
133
|
-
return markers.get(marker) ?? null;
|
|
112
|
+
calendarRows.push({ note, declared, epochOffset });
|
|
134
113
|
}
|
|
135
|
-
|
|
136
|
-
for (const
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
114
|
+
|
|
115
|
+
for (const { note, declared, epochOffset } of calendarRows) {
|
|
116
|
+
const nullRows = declared.filter((row) => row.start === null);
|
|
117
|
+
const firstRows = declared.filter((row) => row.start === 1);
|
|
118
|
+
if (nullRows.length !== 1)
|
|
119
|
+
error(
|
|
120
|
+
note,
|
|
121
|
+
nullRows[0]?.position ?? 0,
|
|
122
|
+
"start",
|
|
123
|
+
"a calendar needs exactly one start: null era before year 1",
|
|
124
|
+
);
|
|
125
|
+
if (firstRows.length !== 1)
|
|
126
|
+
error(
|
|
127
|
+
note,
|
|
128
|
+
firstRows[0]?.position ?? 0,
|
|
129
|
+
"start",
|
|
130
|
+
"a calendar needs exactly one era with start: 1",
|
|
131
|
+
);
|
|
132
|
+
const byStart = new Map();
|
|
133
|
+
for (const row of declared) {
|
|
134
|
+
if (row.start !== null && (!Number.isSafeInteger(row.start) || row.start < 1)) {
|
|
135
|
+
error(
|
|
136
|
+
note,
|
|
137
|
+
row.position,
|
|
138
|
+
"start",
|
|
139
|
+
"an era start is null or a positive integer calendar year",
|
|
140
|
+
);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (byStart.has(row.start)) {
|
|
144
|
+
error(note, row.position, "start", `calendar eras cannot share start ${row.start}`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
byStart.set(row.start, row);
|
|
157
148
|
}
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
daysPerYear,
|
|
149
|
+
const ordered = [...byStart.values()].sort((a, b) =>
|
|
150
|
+
a.start === null ? -1
|
|
151
|
+
: b.start === null ? 1
|
|
152
|
+
: a.start - b.start,
|
|
163
153
|
);
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
154
|
+
for (const [position, row] of ordered.entries()) {
|
|
155
|
+
const startOffset =
|
|
156
|
+
row.start === null ? epochOffset : epochOffset + (row.start - 1) * daysPerYear;
|
|
157
|
+
if (!Number.isSafeInteger(startOffset)) {
|
|
158
|
+
error(
|
|
159
|
+
note,
|
|
160
|
+
row.position,
|
|
161
|
+
"start",
|
|
162
|
+
"era start lies outside the supported day range",
|
|
163
|
+
);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const startDate = canonicalDateFromOffset(startOffset, 1, daysPerYear);
|
|
167
|
+
const next = ordered[position + 1];
|
|
168
|
+
const endOffset = next ? epochOffset + (next.start - 1) * daysPerYear - 1 : undefined;
|
|
169
|
+
const entry = {
|
|
170
|
+
era: row.era,
|
|
171
|
+
qualifier: row.key,
|
|
172
|
+
marker: row.marker,
|
|
173
|
+
firstEra: row.start === null,
|
|
174
|
+
beforeEra: row.start === null,
|
|
175
|
+
startYear: row.start,
|
|
176
|
+
calendar: row.calendar,
|
|
177
|
+
calendarShortcode: row.calendarShortcode,
|
|
178
|
+
name: row.name,
|
|
179
|
+
abbreviation: row.abbreviation,
|
|
180
|
+
label: row.label,
|
|
181
|
+
epochYear: startDate.year,
|
|
182
|
+
epochDay: startDate.day,
|
|
183
|
+
calendarEpochOffset: epochOffset,
|
|
184
|
+
startOffset,
|
|
185
|
+
...(endOffset === undefined ? {} : { endOffset }),
|
|
186
|
+
};
|
|
187
|
+
eras.set(row.key, entry);
|
|
188
|
+
if (row.marker && markerOwners.get(row.marker) === row.key)
|
|
189
|
+
markers.set(row.marker, entry);
|
|
178
190
|
}
|
|
179
|
-
entry.endOffset = endOffset;
|
|
180
191
|
}
|
|
181
|
-
|
|
192
|
+
for (const [alias, key] of qualifiers) {
|
|
193
|
+
if (key === null) eras.set(alias, { ambiguous: true });
|
|
194
|
+
else if (eras.has(key)) eras.set(alias, eras.get(key));
|
|
195
|
+
}
|
|
196
|
+
return { eras, markers, findings };
|
|
182
197
|
}
|
|
183
198
|
|
|
184
|
-
/** One resolved date context per corpus index
|
|
199
|
+
/** One resolved date context per corpus index. */
|
|
185
200
|
const contexts = new WeakMap();
|
|
186
201
|
|
|
187
|
-
/**
|
|
188
|
-
* Read the world's year length and its era markers from one corpus index.
|
|
189
|
-
*
|
|
190
|
-
* @param {{notes?: readonly object[]}|undefined} index - The corpus index.
|
|
191
|
-
* @returns {{markers: Map<string, object>, daysPerYear: number|undefined, findings: object[]}}
|
|
192
|
-
*/
|
|
202
|
+
/** Read the world's year length and resolve every calendar era in one corpus. */
|
|
193
203
|
export function reckoningContext(index) {
|
|
194
204
|
if (!index || typeof index !== "object")
|
|
195
|
-
return { markers: new Map(), daysPerYear: undefined, findings: [] };
|
|
205
|
+
return { eras: new Map(), markers: new Map(), daysPerYear: undefined, findings: [] };
|
|
196
206
|
const cached = contexts.get(index);
|
|
197
207
|
if (cached) return cached;
|
|
198
208
|
const world = (index.notes ?? []).find((note) => {
|
package/engine/scenes.mjs
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* rather than stored. It is the same vocabulary the link manifest uses.
|
|
40
40
|
*
|
|
41
41
|
* Not a standalone script — exports the `Scenes` compiler class, imported and
|
|
42
|
-
* driven by `packages/
|
|
42
|
+
* driven by `packages/package-build/engine/generate.mjs` (via `npm run build:compiledb`).
|
|
43
43
|
*
|
|
44
44
|
* The walk itself — filtering by type, expanding tables, converting
|
|
45
45
|
* wikilinks, writing the JSON and counting errors — belongs to {@link sohl.utils.packs.BasePackCompiler}; this module
|
|
@@ -499,7 +499,10 @@ export class Scenes extends BasePackCompiler {
|
|
|
499
499
|
// default JournalEntry pack, not in whichever Scene pack the map
|
|
500
500
|
// itself was routed to.
|
|
501
501
|
// This compile's router, as everywhere else in this pass.
|
|
502
|
-
journalPack:
|
|
502
|
+
journalPack:
|
|
503
|
+
hasBody ?
|
|
504
|
+
this.#packRouter.resolve(fm, "JournalEntry")
|
|
505
|
+
: this.#packRouter.defaultOf("JournalEntry"),
|
|
503
506
|
pageIds: hasBody ? this.#pageIds(markdown, entryId, name) : new Map(),
|
|
504
507
|
knownActions: this.knownActions,
|
|
505
508
|
warnings,
|
package/engine/schema-check.mjs
CHANGED
|
@@ -468,7 +468,7 @@ export function compareEmittedSystem({
|
|
|
468
468
|
*
|
|
469
469
|
* - **A system**: its own `schema.json`, generated from its `src/` into
|
|
470
470
|
* `build/` by `package-build schema`.
|
|
471
|
-
* - **A module**: the copy cached by `
|
|
471
|
+
* - **A module**: the copy cached by `package-build deps fetch`, from the
|
|
472
472
|
* archive of the version it pins — which is what makes the comparison happen
|
|
473
473
|
* at `verified` rather than against whatever the system's `main` holds today.
|
|
474
474
|
* That distinction is the whole of the `affiliation.subType` case.
|
|
@@ -640,7 +640,7 @@ function schemaFor(config, system = undefined) {
|
|
|
640
640
|
* version released before the artifact existed, or a subtype the artifact does
|
|
641
641
|
* not name, produces no findings — the same stance `compareFields` takes, where
|
|
642
642
|
* an unknown subtype is a routing question rather than a field one. The
|
|
643
|
-
* whole-build check in `
|
|
643
|
+
* whole-build check in `package-build lint` is where a missing artifact is said
|
|
644
644
|
* out loud, once, instead of per note.
|
|
645
645
|
*
|
|
646
646
|
* @param {object} fm - The note's frontmatter.
|
|
@@ -690,7 +690,7 @@ export function checkAuthoredSystemData(
|
|
|
690
690
|
* **Silent where there is nothing to check against**, exactly as its two
|
|
691
691
|
* siblings are: a module pinning a system version released before the artifact
|
|
692
692
|
* existed, or a subtype the artifact does not name, produces no findings.
|
|
693
|
-
* `
|
|
693
|
+
* `package-build lint` is where a missing artifact is said out loud, once.
|
|
694
694
|
*
|
|
695
695
|
* @param {object} emitted - The `system` block the compiler produced.
|
|
696
696
|
* @param {object} opts
|