@heroiclands/package-build 22.28.0 → 23.1.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 +72 -0
- package/README.md +1 -1
- package/bin/content-commands.mjs +1 -1
- package/ci/content-format-reference.mjs +46 -0
- package/docs/api.md +125 -142
- package/docs/authoring/assets.md +1 -1
- package/docs/authoring/dates-and-calendars.md +12 -6
- package/docs/authoring/first-note.md +1 -1
- package/docs/authoring/frontmatter.md +2 -2
- package/docs/authoring/links-and-markup.md +145 -29
- package/docs/commands.md +8 -10
- package/docs/configuration.md +13 -11
- package/docs/content-format.md +3 -3
- package/docs/guides/book.md +59 -4
- package/docs/guides/icons.md +4 -5
- package/docs/guides/packs.md +6 -0
- package/docs/guides/reference-pages.md +21 -1
- package/docs/guides/release-and-deploy.md +17 -1
- package/docs/guides/site.md +17 -18
- package/docs/reference/format-details.md +904 -275
- package/docs/reference/note-types.md +282 -91
- package/docs/reference/theme-pages.md +58 -66
- package/engine/address-values.mjs +124 -4
- package/engine/address.mjs +44 -3
- package/engine/anchors.mjs +103 -3
- package/engine/art-slots.mjs +2 -9
- package/engine/base-compiler.mjs +25 -6
- package/engine/calendar-artifacts.mjs +4 -1
- package/engine/calendar-notes.mjs +166 -102
- package/engine/content-format-check.mjs +2 -3
- package/engine/content-format.mjs +44 -1
- package/engine/content-format.yaml +84 -16
- package/engine/content-images.mjs +10 -1
- package/engine/content-index.mjs +37 -9
- package/engine/content-links.mjs +24 -5
- package/engine/content-tables.mjs +24 -3
- package/engine/data-keys.mjs +347 -0
- package/engine/derived-sections.mjs +322 -0
- package/engine/diagnostics.mjs +28 -0
- package/engine/event-fields.mjs +187 -0
- package/engine/event-rows.mjs +126 -0
- package/engine/event-views.mjs +305 -0
- package/engine/exported-scene.mjs +29 -53
- package/engine/frontmatter-lint.mjs +103 -77
- package/engine/frontmatter.mjs +7 -2
- package/engine/generate.mjs +53 -0
- package/engine/generated-sections.mjs +223 -0
- package/engine/heading-attributes.mjs +4 -2
- package/engine/helpers.mjs +12 -1
- package/engine/holdings.mjs +12 -33
- package/engine/ids.mjs +17 -11
- package/engine/index.mjs +4 -4
- package/engine/infobox-render.mjs +21 -6
- package/engine/infobox.mjs +217 -99
- package/engine/journals.mjs +104 -47
- package/engine/literature-notes.mjs +136 -0
- package/engine/literature-works.mjs +161 -0
- package/engine/map-graphviz.mjs +4 -18
- package/engine/markdown-expressions.mjs +116 -11
- package/engine/metadata-index.mjs +34 -0
- package/engine/near-miss.mjs +90 -0
- package/engine/note-addresses.mjs +75 -19
- package/engine/note-dates.mjs +5 -3
- package/engine/note-event-terms.mjs +364 -0
- package/engine/note-events.mjs +823 -0
- package/engine/note-vocabulary.mjs +195 -92
- package/engine/office-holders.mjs +20 -54
- package/engine/packagebuild-index.mjs +16 -1
- package/engine/pdf-build.mjs +97 -60
- package/engine/pdf-index.mjs +263 -0
- package/engine/pdf-render.mjs +47 -5
- package/engine/place-relation-terms.mjs +174 -0
- package/engine/place-relations.mjs +72 -160
- package/engine/rank-ladder.mjs +14 -41
- package/engine/reckoning-markers.mjs +2 -0
- package/engine/region-events.mjs +6 -12
- package/engine/scenes.mjs +73 -468
- package/engine/site-build.mjs +74 -85
- package/engine/site-index.mjs +3 -1
- package/engine/site-maps.mjs +43 -29
- package/engine/site-preview.mjs +40 -31
- package/engine/social-ties.mjs +2 -1
- package/engine/sql-tables.mjs +212 -9
- package/engine/standing-terms.mjs +145 -6
- package/engine/standings.mjs +37 -49
- package/engine/stub-lint.mjs +6 -0
- package/engine/web-wikilinks.mjs +21 -1
- package/engine/wikilink-syntax.mjs +34 -4
- package/engine/wikilinks.mjs +27 -2
- package/hugo-theme/layouts/partials/infobox/entry.html +4 -1
- package/hugo-theme/layouts/partials/page-body.html +0 -2
- package/hugo-theme/static/css/style.css +25 -55
- package/package.json +1 -2
- package/packagebuild-metadata.jsonl +13 -13
- package/sohl/default-item-art.mjs +6 -6
- package/sohl/note-schemas.mjs +8 -73
- package/types/engine/address-values.d.mts +50 -3
- package/types/engine/address.d.mts +21 -0
- package/types/engine/anchors.d.mts +49 -2
- package/types/engine/art-slots.d.mts +1 -1
- package/types/engine/base-compiler.d.mts +13 -4
- package/types/engine/calendar-notes.d.mts +7 -0
- package/types/engine/content-format.d.mts +52 -0
- package/types/engine/content-images.d.mts +5 -1
- package/types/engine/content-tables.d.mts +9 -1
- package/types/engine/data-keys.d.mts +145 -0
- package/types/engine/derived-sections.d.mts +73 -0
- package/types/engine/diagnostics.d.mts +15 -0
- package/types/engine/event-fields.d.mts +99 -0
- package/types/engine/event-rows.d.mts +12 -0
- package/types/engine/event-views.d.mts +67 -0
- package/types/engine/exported-scene.d.mts +5 -2
- package/types/engine/frontmatter-lint.d.mts +3 -3
- package/types/engine/generated-sections.d.mts +143 -0
- package/types/engine/holdings.d.mts +6 -20
- package/types/engine/ids.d.mts +15 -12
- package/types/engine/index.d.mts +1 -1
- package/types/engine/infobox.d.mts +13 -3
- package/types/engine/journals.d.mts +24 -11
- package/types/engine/literature-notes.d.mts +29 -0
- package/types/engine/literature-works.d.mts +67 -0
- package/types/engine/map-graphviz.d.mts +2 -10
- package/types/engine/markdown-expressions.d.mts +12 -3
- package/types/engine/near-miss.d.mts +42 -0
- package/types/engine/note-addresses.d.mts +7 -1
- package/types/engine/note-event-terms.d.mts +68 -0
- package/types/engine/note-events.d.mts +71 -0
- package/types/engine/note-vocabulary.d.mts +37 -8
- package/types/engine/office-holders.d.mts +3 -0
- package/types/engine/pdf-index.d.mts +35 -0
- package/types/engine/pdf-render.d.mts +8 -2
- package/types/engine/place-relation-terms.d.mts +83 -0
- package/types/engine/place-relations.d.mts +11 -66
- package/types/engine/rank-ladder.d.mts +2 -0
- package/types/engine/region-events.d.mts +6 -12
- package/types/engine/scenes.d.mts +6 -32
- package/types/engine/site-build.d.mts +8 -22
- package/types/engine/site-maps.d.mts +15 -7
- package/types/engine/sql-tables.d.mts +77 -2
- package/types/engine/standing-terms.d.mts +37 -4
- package/types/engine/standings.d.mts +14 -9
- package/types/engine/wikilink-syntax.d.mts +20 -0
- package/types/engine/wikilinks.d.mts +3 -2
- package/docs/guides/government-migration.md +0 -52
- package/engine/itinerary-scenes.mjs +0 -166
- package/engine/lore-events.mjs +0 -197
- package/engine/map-notes.mjs +0 -1309
- package/engine/map-raster.mjs +0 -51
- package/hugo-theme/layouts/partials/holdings/entry.html +0 -21
- package/hugo-theme/layouts/partials/holdings/kind-groups.html +0 -71
- package/hugo-theme/layouts/partials/holdings.html +0 -61
- package/hugo-theme/layouts/partials/place-map.html +0 -9
- package/types/engine/itinerary-scenes.d.mts +0 -29
- package/types/engine/lore-events.d.mts +0 -14
- package/types/engine/map-notes.d.mts +0 -297
- package/types/engine/map-raster.d.mts +0 -8
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* A drawing as the page writer receives it.
|
|
3
3
|
*
|
|
4
4
|
* @typedef {object} SiteMap
|
|
5
|
-
* @property {string} name - The file's name
|
|
6
|
-
* value of the page's `map` key: `from-<shortcode>.svg`.
|
|
5
|
+
* @property {string} name - The file's name: `from-<shortcode>.svg`.
|
|
7
6
|
* @property {string} file - Where the drawing is, absolute.
|
|
8
7
|
*/
|
|
9
8
|
/**
|
|
@@ -24,7 +23,7 @@
|
|
|
24
23
|
*/
|
|
25
24
|
export function inlineSvg(svg: string): string;
|
|
26
25
|
/**
|
|
27
|
-
* Draw the map from every
|
|
26
|
+
* Draw the map from every place of this package that is given one.
|
|
28
27
|
*
|
|
29
28
|
* @param {object} opts
|
|
30
29
|
* @param {Array<Record<string, any>>} opts.records - The package's index
|
|
@@ -36,8 +35,8 @@ export function inlineSvg(svg: string): string;
|
|
|
36
35
|
* ending in a slash: what every name in a drawing links through.
|
|
37
36
|
* @returns {{maps: Map<string, SiteMap>, findings: Array<{file: string,
|
|
38
37
|
* line?: number, column?: number, severity: "error"|"warning",
|
|
39
|
-
* message: string}>}} The drawings, keyed by the
|
|
40
|
-
*
|
|
38
|
+
* message: string}>}} The drawings, keyed by the file name a **From here**
|
|
39
|
+
* section names, and warnings for relations that name no place.
|
|
41
40
|
*/
|
|
42
41
|
export function drawSiteMaps({ records, foreignIndex, config, base }: {
|
|
43
42
|
records: Array<Record<string, any>>;
|
|
@@ -54,6 +53,16 @@ export function drawSiteMaps({ records, foreignIndex, config, base }: {
|
|
|
54
53
|
message: string;
|
|
55
54
|
}>;
|
|
56
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* The figure the website sets a drawing in: the SVG inline, so its place names
|
|
58
|
+
* stay links, with no blank line inside it — a blank line would end the HTML
|
|
59
|
+
* block and hand the rest of the drawing to the Markdown renderer.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} svg - The drawing, as {@link inlineSvg} wrote it.
|
|
62
|
+
* @param {string} alt - What the drawing shows, for a screen reader.
|
|
63
|
+
* @returns {string} One HTML block.
|
|
64
|
+
*/
|
|
65
|
+
export function inlineDrawingFigure(svg: string, alt: string): string;
|
|
57
66
|
/**
|
|
58
67
|
* Where the site build draws, below the package root.
|
|
59
68
|
*
|
|
@@ -65,8 +74,7 @@ export const SITE_MAP_DIR: string;
|
|
|
65
74
|
*/
|
|
66
75
|
export type SiteMap = {
|
|
67
76
|
/**
|
|
68
|
-
* - The file's name
|
|
69
|
-
* value of the page's `map` key: `from-<shortcode>.svg`.
|
|
77
|
+
* - The file's name: `from-<shortcode>.svg`.
|
|
70
78
|
*/
|
|
71
79
|
name: string;
|
|
72
80
|
/**
|
|
@@ -164,6 +164,77 @@ export function prepareSqlTables(db: object, sources: Array<{
|
|
|
164
164
|
}): Promise<Map<string, object[]>>;
|
|
165
165
|
/** Evaluate scalar SQL calls before synchronous Markdown renderers run. */
|
|
166
166
|
export function prepareInlineSqlExpressions(db: any, sources: any): Promise<Map<any, any>>;
|
|
167
|
+
/**
|
|
168
|
+
* Each note's generated sections — the derived lists and the event views — see
|
|
169
|
+
* {@link module:engine/generated-sections}.
|
|
170
|
+
*
|
|
171
|
+
* @param {object|undefined} db - From {@link openNotesDatabase}, over
|
|
172
|
+
* `records`; absent, no event view is asked for.
|
|
173
|
+
* @param {object[]} records - Content-index records, of the audience the
|
|
174
|
+
* surface publishes to.
|
|
175
|
+
* @param {string} contentBase - Root of the content tree.
|
|
176
|
+
* @param {object} [opts]
|
|
177
|
+
* @param {string} [opts.only] - One note file to give sections to; absent,
|
|
178
|
+
* every note.
|
|
179
|
+
* @param {Map<string, object>} [opts.foreignIndex] - The fetched indexes, whose
|
|
180
|
+
* places, affiliations and works the lists take in.
|
|
181
|
+
* @param {object} [opts.config] - The resolved configuration.
|
|
182
|
+
* @param {Map<string, Map<string, string>>} [opts.derived] - The derived
|
|
183
|
+
* lists, from an earlier call over the same records, which are not derived
|
|
184
|
+
* again.
|
|
185
|
+
* @returns {Promise<{sections: Map<string, string>, views: Set<string>,
|
|
186
|
+
* derived: Map<string, Map<string, string>>}>} Note file to its sections,
|
|
187
|
+
* joined in order; the files whose sections hold an event view's fence; and
|
|
188
|
+
* the derived lists alone.
|
|
189
|
+
*/
|
|
190
|
+
export function generatedSectionsFor(db: object | undefined, records: object[], contentBase: string, { only, foreignIndex, config, derived: given }?: {
|
|
191
|
+
only?: string | undefined;
|
|
192
|
+
foreignIndex?: Map<string, object> | undefined;
|
|
193
|
+
config?: object | undefined;
|
|
194
|
+
derived?: Map<string, Map<string, string>> | undefined;
|
|
195
|
+
}): Promise<{
|
|
196
|
+
sections: Map<string, string>;
|
|
197
|
+
views: Set<string>;
|
|
198
|
+
derived: Map<string, Map<string, string>>;
|
|
199
|
+
}>;
|
|
200
|
+
/**
|
|
201
|
+
* Join each note's generated sections to its body among the sources a
|
|
202
|
+
* preparation answers, so the fences the event views hold are answered with
|
|
203
|
+
* the author's own, in order after them. A note whose sections hold a fence
|
|
204
|
+
* and whose body holds no directive of its own joins the sources, its body
|
|
205
|
+
* read as {@link prepareTreeSqlTables} reads one.
|
|
206
|
+
*
|
|
207
|
+
* The expansion every surface runs appends the same text again, from the
|
|
208
|
+
* `generated` {@link attachGeneratedSections} sets on the note's results —
|
|
209
|
+
* see {@link module:engine/content-tables.expandContentTables}.
|
|
210
|
+
*
|
|
211
|
+
* @param {{sections: Map<string, string>, views: Set<string>}} generated -
|
|
212
|
+
* From {@link generatedSectionsFor}.
|
|
213
|
+
* @param {Array<{source: string, markdown: string, frontmatter?: object}>} sources -
|
|
214
|
+
* The bodies to answer, extended in place.
|
|
215
|
+
* @returns {void}
|
|
216
|
+
*/
|
|
217
|
+
export function joinGeneratedSections(generated: {
|
|
218
|
+
sections: Map<string, string>;
|
|
219
|
+
views: Set<string>;
|
|
220
|
+
}, sources: Array<{
|
|
221
|
+
source: string;
|
|
222
|
+
markdown: string;
|
|
223
|
+
frontmatter?: object;
|
|
224
|
+
}>): void;
|
|
225
|
+
/**
|
|
226
|
+
* Record each note's generated sections on its prepared results, where the
|
|
227
|
+
* table expansion reads them. A note with no results of its own is given an
|
|
228
|
+
* empty set carrying them.
|
|
229
|
+
*
|
|
230
|
+
* @param {Map<string, object[]>} prepared - From {@link prepareSqlTables}.
|
|
231
|
+
* @param {{sections: Map<string, string>}} generated - From
|
|
232
|
+
* {@link generatedSectionsFor}.
|
|
233
|
+
* @returns {void}
|
|
234
|
+
*/
|
|
235
|
+
export function attachGeneratedSections(prepared: Map<string, object[]>, generated: {
|
|
236
|
+
sections: Map<string, string>;
|
|
237
|
+
}): void;
|
|
167
238
|
/**
|
|
168
239
|
* Answer every `sql` directive in a content tree.
|
|
169
240
|
*
|
|
@@ -189,15 +260,19 @@ export function prepareInlineSqlExpressions(db: any, sources: any): Promise<Map<
|
|
|
189
260
|
* A command that also builds a link index holds them already, and deriving
|
|
190
261
|
* them twice is the duplicated-corpus failure this closes.
|
|
191
262
|
* @param {"all"|"public"} [opts.audience] - Whether to exclude GM notes.
|
|
263
|
+
* @param {Map<string, object>} [opts.foreignIndex] - The fetched indexes the
|
|
264
|
+
* generated sections take in, where the caller holds them already; absent,
|
|
265
|
+
* they are read from the cache.
|
|
192
266
|
* @returns {Promise<Map<string, object[]>|undefined>} Results by note path,
|
|
193
267
|
* carrying `inline` and `pageLists` beside them, or nothing when the tree
|
|
194
|
-
* holds no such directive.
|
|
268
|
+
* holds no such directive and no note is given a generated section.
|
|
195
269
|
*/
|
|
196
|
-
export function prepareTreeSqlTables(contentBase: string, { config, skipDirectories, records, audience }?: {
|
|
270
|
+
export function prepareTreeSqlTables(contentBase: string, { config, skipDirectories, records, audience, foreignIndex }?: {
|
|
197
271
|
config?: object | undefined;
|
|
198
272
|
skipDirectories?: readonly string[] | undefined;
|
|
199
273
|
records?: object[] | undefined;
|
|
200
274
|
audience?: "all" | "public" | undefined;
|
|
275
|
+
foreignIndex?: Map<string, object> | undefined;
|
|
201
276
|
}): Promise<Map<string, object[]> | undefined>;
|
|
202
277
|
/**
|
|
203
278
|
* Aliases the renderer reads rather than printing.
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terms of a body's standing — what a membership carries, and the rungs
|
|
3
|
+
* and posts a body confers — as inner-key declarations.
|
|
4
|
+
*
|
|
5
|
+
* A leaf module, importing nothing: the note vocabulary reads these
|
|
6
|
+
* declarations as it loads, and the checks that judge what the values mean
|
|
7
|
+
* reach the vocabulary through the Address reader.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
1
11
|
/**
|
|
2
12
|
* The note type a standing is held in.
|
|
3
13
|
*
|
|
@@ -7,10 +17,33 @@
|
|
|
7
17
|
*/
|
|
8
18
|
export const STANDING_BODY_TYPES: readonly string[];
|
|
9
19
|
/**
|
|
10
|
-
* The
|
|
20
|
+
* The keys one standing carries, as inner-key declarations.
|
|
11
21
|
*
|
|
12
|
-
* `rank` is the level on the body's own `governance.ranks` ladder,
|
|
13
|
-
* `office` is a key of its `governance.offices` map,
|
|
14
|
-
*
|
|
22
|
+
* `rank` is the level on the body's own `governance.ranks` ladder, and every
|
|
23
|
+
* membership states one. `office` is a key of its `governance.offices` map, and
|
|
24
|
+
* is stated only where the being holds a post.
|
|
25
|
+
*
|
|
26
|
+
* @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
|
|
15
27
|
*/
|
|
28
|
+
export const STANDING_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
|
|
29
|
+
/** The keys of {@link STANDING_FIELDS}, in declared order. */
|
|
16
30
|
export const STANDING_KEYS: readonly string[];
|
|
31
|
+
/**
|
|
32
|
+
* The keys a rung carries, as inner-key declarations.
|
|
33
|
+
*
|
|
34
|
+
* @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
|
|
35
|
+
*/
|
|
36
|
+
export const RUNG_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
|
|
37
|
+
/**
|
|
38
|
+
* The keys one row of an office's `holders` carries, as inner-key declarations.
|
|
39
|
+
*
|
|
40
|
+
* @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
|
|
41
|
+
*/
|
|
42
|
+
export const HOLDER_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
|
|
43
|
+
/**
|
|
44
|
+
* The keys an office written as a map carries. An office may instead be its
|
|
45
|
+
* description alone, as a string.
|
|
46
|
+
*
|
|
47
|
+
* @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
|
|
48
|
+
*/
|
|
49
|
+
export const OFFICE_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
|
|
@@ -101,22 +101,25 @@ export function standingsDigest(fm: object): {
|
|
|
101
101
|
offices?: Record<string, string>;
|
|
102
102
|
} | undefined;
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
104
|
+
* What a standing says before the body that confers it — `War Chief, Hárár
|
|
105
|
+
* (5), of ` — or `""` where no standing is held.
|
|
105
106
|
*
|
|
106
107
|
* The office leads, because it is the more particular of the two: a reader
|
|
107
|
-
* meeting "War Chief" has been told the rung as well. The
|
|
108
|
-
*
|
|
109
|
-
* no
|
|
108
|
+
* meeting "War Chief" has been told the rung as well. The rung follows as its
|
|
109
|
+
* title with its level in parentheses, read from the body's own ladder; where
|
|
110
|
+
* the ladder names no rung at that level, or none is available, it reads as
|
|
111
|
+
* `Rank 5` and the number is not repeated. The body closes the phrase, which is
|
|
112
|
+
* the whole point of keying the entry by it — a standing with no body named is
|
|
113
|
+
* the lossy form.
|
|
110
114
|
*
|
|
111
115
|
* No article is supplied. One body's name reads with a "the" in front of it and
|
|
112
116
|
* the next does not, and inventing one is this build editing the corpus.
|
|
113
117
|
*
|
|
114
118
|
* @param {object} standing - `{rank, office}`, either or both absent.
|
|
115
|
-
* @param {string} body - What the body is called.
|
|
116
119
|
* @param {{ranks?: Record<string, string>}} [digest] - The body's ladder.
|
|
117
|
-
* @returns {string} The
|
|
120
|
+
* @returns {string} The words before the body's name, ending `of `.
|
|
118
121
|
*/
|
|
119
|
-
export function
|
|
122
|
+
export function standingLead(standing: object, digest?: {
|
|
120
123
|
ranks?: Record<string, string>;
|
|
121
124
|
}): string;
|
|
122
125
|
/**
|
|
@@ -127,7 +130,8 @@ export function standingPhrase(standing: object, body: string, digest?: {
|
|
|
127
130
|
*
|
|
128
131
|
* 1. the value is a map keyed by Address (or the list this window still takes);
|
|
129
132
|
* 2. every key names an affiliation that resolves, once;
|
|
130
|
-
* 3. the entry states a
|
|
133
|
+
* 3. the entry states a standing at all — what a stated standing holds is the
|
|
134
|
+
* inner-key check's, read from {@link STANDING_FIELDS};
|
|
131
135
|
* 4. a `rank` is a level the named body's `governance.ranks` declares;
|
|
132
136
|
* 5. an `office` is a key of that body's `governance.offices`.
|
|
133
137
|
*
|
|
@@ -143,5 +147,6 @@ export function checkStandings(note: object, { index }?: {
|
|
|
143
147
|
index?: object;
|
|
144
148
|
}): object[];
|
|
145
149
|
import { STANDING_BODY_TYPES } from "./standing-terms.mjs";
|
|
150
|
+
import { STANDING_FIELDS } from "./standing-terms.mjs";
|
|
146
151
|
import { STANDING_KEYS } from "./standing-terms.mjs";
|
|
147
|
-
export { STANDING_BODY_TYPES, STANDING_KEYS };
|
|
152
|
+
export { STANDING_BODY_TYPES, STANDING_FIELDS, STANDING_KEYS };
|
|
@@ -72,6 +72,26 @@ export function authoredLabel({ display }: {
|
|
|
72
72
|
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
73
73
|
*/
|
|
74
74
|
export function unlabelledLinkMessage(target: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Whether an anchor a link names is one of its target's events.
|
|
77
|
+
*
|
|
78
|
+
* A wikilink names a `prose` anchor; an `event` anchor is named only by the
|
|
79
|
+
* frontmatter keys that accept one. A local target carries its event ids as
|
|
80
|
+
* `eventAnchors`, a target another package publishes carries its kinded
|
|
81
|
+
* anchors as `noteAnchors`.
|
|
82
|
+
*
|
|
83
|
+
* @param {{eventAnchors?: Set<string>, noteAnchors?: Array<{slug: string,
|
|
84
|
+
* kind?: string}>}|undefined} hit - The link's resolved target.
|
|
85
|
+
* @param {string} anchor - The anchor the link names.
|
|
86
|
+
* @returns {boolean} Whether it is an event.
|
|
87
|
+
*/
|
|
88
|
+
export function isEventAnchor(hit: {
|
|
89
|
+
eventAnchors?: Set<string>;
|
|
90
|
+
noteAnchors?: Array<{
|
|
91
|
+
slug: string;
|
|
92
|
+
kind?: string;
|
|
93
|
+
}>;
|
|
94
|
+
} | undefined, anchor: string): boolean;
|
|
75
95
|
/**
|
|
76
96
|
* What an author writing an address that resolves to nothing is told.
|
|
77
97
|
*
|
|
@@ -18,8 +18,8 @@ export function anchorPageId(noteId: string, anchorSlug: string): string;
|
|
|
18
18
|
*
|
|
19
19
|
* @param {Array<{type: string, id: string, shortcode?: string|null,
|
|
20
20
|
* name?: string, pack?: string, docPack?: string, none?: boolean,
|
|
21
|
-
* draft?: boolean, anchors?: Set<string>,
|
|
22
|
-
* docAnchorUuids?: Record<string,string>}>} docs -
|
|
21
|
+
* draft?: boolean, anchors?: Set<string>, eventAnchors?: Set<string>,
|
|
22
|
+
* anchorUuids?: Record<string,string>, docAnchorUuids?: Record<string,string>}>} docs -
|
|
23
23
|
* One entry per content note. `pack` / `docPack` name the packs the note's
|
|
24
24
|
* document and its documentation entry landed in; omitted, the conventional
|
|
25
25
|
* one-pack-per-type names stand in. `none` says the note declares
|
|
@@ -56,6 +56,7 @@ export function buildWikilinkIndex(docs: Array<{
|
|
|
56
56
|
none?: boolean;
|
|
57
57
|
draft?: boolean;
|
|
58
58
|
anchors?: Set<string>;
|
|
59
|
+
eventAnchors?: Set<string>;
|
|
59
60
|
anchorUuids?: Record<string, string>;
|
|
60
61
|
docAnchorUuids?: Record<string, string>;
|
|
61
62
|
}>, packageId: string, foreign?: Map<string, object>, contentPackage?: string, { assets, noIndexPackages, referenceTargets, foreignReferences }?: {
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
shortcode: governmentmigration
|
|
3
|
-
name: { full: "Migrating place governments" }
|
|
4
|
-
type: doc
|
|
5
|
-
subType: howto
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Migrating place governments
|
|
9
|
-
|
|
10
|
-
A place's `data.government` is the sole declaration of government. Its type is `Address`, with default target type `affiliation`. Name one governing affiliation, or write explicit `null` for complete anarchy. Omission means unknown. The website derives `governed_by` on a place and `governed_places` on its governing affiliation from resolved references across local and fetched indexes. Neither containment nor organizational parents supplies a government.
|
|
11
|
-
|
|
12
|
-
```yaml
|
|
13
|
-
type: place
|
|
14
|
-
data:
|
|
15
|
-
government: temple
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
A temple can be a government when it actually governs the place; so can a company, house or polity. Naming land in an old affiliation `data.domains` list never makes that body a government automatically. When old lists overlap, choose the one body that actually governs each place. Do not choose from subtype or list order. Leave government absent if the answer is unknown; do not write null merely to silence a warning.
|
|
19
|
-
|
|
20
|
-
## Legacy input and output changes
|
|
21
|
-
|
|
22
|
-
Legacy affiliation `data.domains` is temporarily accepted without a finding so existing trees can migrate. It is ignored: it no longer creates affiliation Domains infobox rows, metadata-index `domains`, SoHL `system.domain`, or website `held_by` and `holdings`. New notes should omit it. A fetched older system schema can still declare `system.domain`; the schema check then reports an advisory that the field receives its system default. No new government is inferred from that default. There is no new authored holdings or tenure field. Preserve distinct ownership, subordinate tenure, property rights or influence in the note's prose, with links where useful, before deleting the old list.
|
|
23
|
-
|
|
24
|
-
The tenure-based `over-held land` population advisory is retired. Geographical `contains` and the remaining population checks continue to use place parents unchanged. Explicit null remains null in published metadata and displays Complete anarchy; omission stays absent. Neither appears in a governing body's reverse list.
|
|
25
|
-
|
|
26
|
-
## Inventory behind the migration
|
|
27
|
-
|
|
28
|
-
The baseline at commit `ada039d` contains 15 production files, six documentation files and nine test files mentioning `domains`, `held_by` or `holdings` (137, 70 and 99 occurrences respectively). Domains alone appears in 20 files across 127 matching lines. The active consumers are author declarations/schema and infoboxes, metadata indexes, SoHL `system.domain`, direct and reverse website tenure lists, site previews and the polity population advisory. Public holdings APIs and generated reference documentation describe these outputs.
|
|
29
|
-
|
|
30
|
-
The migrated Thalorna baseline has 3,171 notes: 637 places, of which 191 declare government, 446 omit it and none declare null. Its 133 affiliations still carry 196 legacy domains claims covering 191 distinct places: 125 polities account for 184 claims, seven faith traditions for 11 and one order for one. Two places have overlapping claims (`pssshrines`: two; `aukhelathrgq`: five). These are reasons to review actual governance rather than infer it from legacy claims.
|
|
31
|
-
|
|
32
|
-
Those consumers combined governance with independently meaningful direct tenure: houses holding manors, temples and companies claiming property, overlapping provincial and imperial claims. This migration deliberately replaces structured tenure displays with government derived from places, rather than converting every old claim into government. Independent historical facts remain supported in prose. Acceptance of legacy input is transitional; enforced removal will require separate author guidance.
|
|
33
|
-
|
|
34
|
-
## SQL queries
|
|
35
|
-
|
|
36
|
-
Queries that read affiliation `data.domains` must instead join each place's explicit government to the affiliation's address. For example, to list the governing bodies of places directly within a region:
|
|
37
|
-
|
|
38
|
-
```sql
|
|
39
|
-
SELECT DISTINCT affiliation.address.slug AS _ref,
|
|
40
|
-
affiliation.name.full AS "Government"
|
|
41
|
-
FROM notes affiliation
|
|
42
|
-
JOIN notes place
|
|
43
|
-
ON place.data.government = concat(affiliation.package, '-note-affiliation-', affiliation.shortcode)
|
|
44
|
-
WHERE affiliation.type = 'affiliation'
|
|
45
|
-
AND place.type = 'place'
|
|
46
|
-
AND list_contains(place.data.parents, 'thalorna-note-place-aelwyth')
|
|
47
|
-
ORDER BY affiliation.name.full COLLATE NOCASE
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
The equality above uses the canonical journal addresses in the local `notes` table. Explicit Item addresses may use a system segment such as `sohl`; include that address when joining those references. Government lists generated by the site already handle Item and journal addresses for the same page. Null and omitted government do not match the join. A query that can legitimately select no places should declare `{allow-empty=true}` on its SQL fence.
|
|
51
|
-
|
|
52
|
-
The real consumer regression migrated fourteen SQL query sites, including ten Government columns that formerly read reverse domains and four polity queries. Keeping the source field accepted does not preserve queries that depend on its retired semantics.
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/** Gridless Foundry Scenes drawn from place relations. */
|
|
6
|
-
|
|
7
|
-
import fs from "node:fs";
|
|
8
|
-
import path from "node:path";
|
|
9
|
-
import { resolvePackageBuildConfig } from "../config.mjs";
|
|
10
|
-
|
|
11
|
-
import { buildMaps, relatedPlaces } from "./map-build.mjs";
|
|
12
|
-
import { renderDot } from "./map-graphviz.mjs";
|
|
13
|
-
import { mapWorld } from "./map-places.mjs";
|
|
14
|
-
import { loadForeignIndexes } from "./metadata-index.mjs";
|
|
15
|
-
import { authoredFrontmatter, isNoteRecord } from "./index-records.mjs";
|
|
16
|
-
import { buildScene } from "./map-notes.mjs";
|
|
17
|
-
import { buildJournalEntry } from "./journals.mjs";
|
|
18
|
-
import { imageDimensions } from "./asset-types.mjs";
|
|
19
|
-
import { makeId } from "./ids.mjs";
|
|
20
|
-
import { stripAdventureKeys } from "./bundle-notes.mjs";
|
|
21
|
-
|
|
22
|
-
/** GraphViz's plain coordinates, converted to pixel positions on its PNG. */
|
|
23
|
-
export function pinPositions(plain, width, height) {
|
|
24
|
-
const graph = /^graph\s+\S+\s+(\S+)\s+(\S+)/m.exec(plain);
|
|
25
|
-
if (!graph) throw new Error("GraphViz emitted no graph size for itinerary pins");
|
|
26
|
-
const graphWidth = Number(graph[1]);
|
|
27
|
-
const graphHeight = Number(graph[2]);
|
|
28
|
-
if (!(graphWidth > 0 && graphHeight > 0)) {
|
|
29
|
-
throw new Error("GraphViz emitted an invalid graph size for itinerary pins");
|
|
30
|
-
}
|
|
31
|
-
const positions = new Map();
|
|
32
|
-
for (const line of plain.split("\n")) {
|
|
33
|
-
const match = /^node\s+(?:"(p_[^"]+)"|(p_\S+))\s+(\S+)\s+(\S+)/.exec(line);
|
|
34
|
-
if (!match) continue;
|
|
35
|
-
const shortcode = (match[1] ?? match[2]).slice(2);
|
|
36
|
-
positions.set(shortcode, {
|
|
37
|
-
x: Math.round((Number(match[3]) / graphWidth) * width),
|
|
38
|
-
y: Math.round((1 - Number(match[4]) / graphHeight) * height),
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return positions;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** The compiled local journals a generated Adventure can import with its Scenes. */
|
|
45
|
-
function localJournals(dirs) {
|
|
46
|
-
const byId = new Map();
|
|
47
|
-
for (const dir of dirs) {
|
|
48
|
-
if (!fs.existsSync(dir)) continue;
|
|
49
|
-
for (const file of fs.readdirSync(dir)) {
|
|
50
|
-
if (!file.endsWith(".json")) continue;
|
|
51
|
-
const doc = JSON.parse(fs.readFileSync(path.join(dir, file), "utf8"));
|
|
52
|
-
if (String(doc._key ?? "").startsWith("!journal!")) byId.set(doc._id, doc);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return byId;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Emit backgrounds into the staged package and return Scenes and the journals
|
|
60
|
-
* their pins need. The caller writes them into its Scene and Adventure packs.
|
|
61
|
-
*/
|
|
62
|
-
export function buildItineraryScenes({
|
|
63
|
-
config,
|
|
64
|
-
records,
|
|
65
|
-
contentBase,
|
|
66
|
-
journalSourceDirs = [],
|
|
67
|
-
stats,
|
|
68
|
-
foreign = loadForeignIndexes(config, [config.contentPackage]),
|
|
69
|
-
}) {
|
|
70
|
-
const buildConfig = resolvePackageBuildConfig(config);
|
|
71
|
-
const world = mapWorld({ records, foreignIndex: foreign.index, contentBase, config });
|
|
72
|
-
const centres = relatedPlaces(world.places).filter((sc) => world.places.get(sc)?.local);
|
|
73
|
-
const findings = foreign.stale.map((stale) => ({
|
|
74
|
-
file: contentBase,
|
|
75
|
-
severity: "warning",
|
|
76
|
-
message: `the ${stale.package} content index is unavailable, so itinerary maps omit its places: ${stale.reason}`,
|
|
77
|
-
}));
|
|
78
|
-
const assetDir = path.resolve(
|
|
79
|
-
config.rootDir,
|
|
80
|
-
buildConfig.stageDir,
|
|
81
|
-
"assets/maps/generated-itineraries",
|
|
82
|
-
);
|
|
83
|
-
fs.rmSync(assetDir, { recursive: true, force: true });
|
|
84
|
-
if (!centres.length) return { scenes: [], journal: [], findings };
|
|
85
|
-
|
|
86
|
-
const outDir = path.resolve(config.rootDir, "build/map/foundry");
|
|
87
|
-
fs.rmSync(outDir, { recursive: true, force: true });
|
|
88
|
-
const drawn = buildMaps({ world, outDir, from: centres });
|
|
89
|
-
findings.push(...drawn.findings);
|
|
90
|
-
fs.mkdirSync(assetDir, { recursive: true });
|
|
91
|
-
|
|
92
|
-
const localRecords = new Map();
|
|
93
|
-
for (const record of records) {
|
|
94
|
-
if (!isNoteRecord(record) || record.type !== "place") continue;
|
|
95
|
-
localRecords.set(String(record.shortcode).toLowerCase(), authoredFrontmatter(record));
|
|
96
|
-
}
|
|
97
|
-
const compiledJournals = localJournals(journalSourceDirs);
|
|
98
|
-
const imported = new Map();
|
|
99
|
-
const scenes = [];
|
|
100
|
-
for (const centre of centres) {
|
|
101
|
-
const dot = path.join(outDir, `from-${centre}.dot`);
|
|
102
|
-
const png = path.join(assetDir, `from-${centre}.png`);
|
|
103
|
-
const plain = path.join(outDir, `from-${centre}.plain`);
|
|
104
|
-
renderDot(dot, png, { engine: "neato", format: "png", nop: 2, dpi: 150 });
|
|
105
|
-
renderDot(dot, plain, { engine: "neato", format: "plain", nop: 2 });
|
|
106
|
-
const size = imageDimensions(png, ".png");
|
|
107
|
-
if (!size) throw new Error(`${png} carries no readable PNG size`);
|
|
108
|
-
const { width, height } = size;
|
|
109
|
-
const positions = pinPositions(fs.readFileSync(plain, "utf8"), width, height);
|
|
110
|
-
const sceneId = makeId("itinerary-scene", `${config.contentPackage}:${centre}`);
|
|
111
|
-
const src = `${buildConfig.packageKind}/${buildConfig.packageId}/assets/maps/generated-itineraries/from-${centre}.png`;
|
|
112
|
-
const scene = buildScene(
|
|
113
|
-
{
|
|
114
|
-
id: sceneId,
|
|
115
|
-
name: { full: `From ${world.places.get(centre).name}` },
|
|
116
|
-
subType: "regionalmap",
|
|
117
|
-
sohl: { dimensions: [width, height], pxPerGrid: 100 },
|
|
118
|
-
data: { bgImage: src },
|
|
119
|
-
},
|
|
120
|
-
{ art: () => src, stats },
|
|
121
|
-
);
|
|
122
|
-
scene.grid.distance = 1;
|
|
123
|
-
scene.grid.units = "";
|
|
124
|
-
for (const [shortcode, at] of positions) {
|
|
125
|
-
const place = world.places.get(shortcode);
|
|
126
|
-
if (!place) continue;
|
|
127
|
-
let journal;
|
|
128
|
-
if (place.local) {
|
|
129
|
-
journal = compiledJournals.get(localRecords.get(shortcode)?.id);
|
|
130
|
-
} else {
|
|
131
|
-
const ref = foreign.index.get(place.address);
|
|
132
|
-
if (ref?.uuid && /\.JournalEntry\./.test(ref.uuid)) {
|
|
133
|
-
const id = makeId("itinerary-foreign-journal", place.address);
|
|
134
|
-
journal = buildJournalEntry({
|
|
135
|
-
id,
|
|
136
|
-
name: place.name,
|
|
137
|
-
markdown: `@UUID[${ref.uuid}]{Open ${place.name}}`,
|
|
138
|
-
stats,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (!journal) {
|
|
143
|
-
findings.push({
|
|
144
|
-
file: place.file ? path.join(contentBase, place.file) : contentBase,
|
|
145
|
-
severity: "warning",
|
|
146
|
-
message: `place "${shortcode}" has no addressable JournalEntry, so its itinerary label has no Foundry pin`,
|
|
147
|
-
});
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
imported.set(journal._id, stripAdventureKeys(journal));
|
|
151
|
-
const id = makeId("itinerary-note", `${sceneId}:${shortcode}`);
|
|
152
|
-
scene.notes.push({
|
|
153
|
-
_id: id,
|
|
154
|
-
entryId: journal._id,
|
|
155
|
-
pageId: null,
|
|
156
|
-
x: at.x,
|
|
157
|
-
y: at.y,
|
|
158
|
-
text: place.name,
|
|
159
|
-
iconSize: 32,
|
|
160
|
-
_key: `!scenes.notes!${sceneId}.${id}`,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
scenes.push(scene);
|
|
164
|
-
}
|
|
165
|
-
return { scenes, journal: [...imported.values()], findings };
|
|
166
|
-
}
|