@heroiclands/package-build 20.4.0 → 20.6.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 (78) hide show
  1. package/CHANGELOG.md +288 -0
  2. package/CONTENT.md +213 -20
  3. package/README.md +19 -1
  4. package/bin/content-build.mjs +135 -32
  5. package/bin/package-build.mjs +46 -13
  6. package/content-config.mjs +345 -101
  7. package/docs/api.md +1352 -0
  8. package/docs/commands.md +1609 -0
  9. package/docs/configuration.md +1432 -0
  10. package/docs/content-format.md +16 -6
  11. package/docs/diagnostics.md +356 -0
  12. package/docs/getting-started.md +813 -0
  13. package/docs/project-setup.md +469 -0
  14. package/engine/actor-compiler.mjs +30 -27
  15. package/engine/address-diff.mjs +45 -41
  16. package/engine/base-compiler.mjs +6 -0
  17. package/engine/bundles.mjs +9 -0
  18. package/engine/content-address.mjs +9 -9
  19. package/engine/content-index.mjs +44 -23
  20. package/engine/content-links.mjs +44 -11
  21. package/engine/content-lint.mjs +44 -10
  22. package/engine/content-tables.mjs +32 -27
  23. package/engine/folder-notes.mjs +4 -2
  24. package/engine/frontmatter-lint.mjs +35 -38
  25. package/engine/generate.mjs +5 -0
  26. package/engine/helpers.mjs +86 -32
  27. package/engine/index.mjs +12 -2
  28. package/engine/journals.mjs +9 -0
  29. package/engine/note-claims.mjs +18 -10
  30. package/engine/note-schemas.mjs +0 -5
  31. package/engine/note-vocabulary.mjs +32 -31
  32. package/engine/pack-config.mjs +26 -12
  33. package/engine/pack-router.mjs +0 -0
  34. package/engine/pdf-build.mjs +464 -0
  35. package/engine/pdf-fonts.mjs +420 -0
  36. package/engine/pdf-render.mjs +876 -0
  37. package/engine/pdf-toc.mjs +525 -0
  38. package/engine/scenes.mjs +14 -5
  39. package/engine/schema-check.mjs +1 -1
  40. package/engine/site-build.mjs +21 -3
  41. package/engine/web-wikilinks.mjs +6 -3
  42. package/engine/wikilinks.mjs +2 -4
  43. package/hm3/actors.mjs +8 -0
  44. package/hm3/items.mjs +8 -0
  45. package/package.json +1 -1
  46. package/release.mjs +63 -3
  47. package/sohl/actors.mjs +8 -0
  48. package/sohl/items.mjs +8 -0
  49. package/sohl/note-schemas.mjs +5 -5
  50. package/types/content-config.d.mts +66 -15
  51. package/types/engine/actor-compiler.d.mts +34 -30
  52. package/types/engine/address-diff.d.mts +57 -3
  53. package/types/engine/base-compiler.d.mts +10 -2
  54. package/types/engine/bundles.d.mts +9 -0
  55. package/types/engine/content-address.d.mts +9 -9
  56. package/types/engine/content-index.d.mts +57 -13
  57. package/types/engine/content-lint.d.mts +6 -4
  58. package/types/engine/content-tables.d.mts +49 -18
  59. package/types/engine/frontmatter-lint.d.mts +3 -2
  60. package/types/engine/helpers.d.mts +105 -31
  61. package/types/engine/index.d.mts +4 -0
  62. package/types/engine/journals.d.mts +9 -0
  63. package/types/engine/note-claims.d.mts +17 -10
  64. package/types/engine/note-vocabulary.d.mts +23 -196
  65. package/types/engine/pack-config.d.mts +4 -4
  66. package/types/engine/pdf-build.d.mts +42 -0
  67. package/types/engine/pdf-fonts.d.mts +30 -0
  68. package/types/engine/pdf-render.d.mts +156 -0
  69. package/types/engine/pdf-toc.d.mts +114 -0
  70. package/types/engine/scenes.d.mts +10 -1
  71. package/types/engine/schema-check.d.mts +2 -2
  72. package/types/engine/site-build.d.mts +34 -6
  73. package/types/engine/wikilinks.d.mts +2 -3
  74. package/types/hm3/actors.d.mts +8 -0
  75. package/types/hm3/items.d.mts +8 -0
  76. package/types/release.d.mts +15 -4
  77. package/types/sohl/actors.d.mts +10 -2
  78. package/types/sohl/items.d.mts +8 -0
@@ -95,11 +95,11 @@ export function locateConfigError(err: unknown, configPath?: string): unknown;
95
95
  *
96
96
  * @param {unknown} data - The parsed configuration document.
97
97
  * @param {string} configPath - Absolute path of the file it was parsed from.
98
- * @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
98
+ * @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
99
99
  * @throws {Error} When the document is not a mapping, declares `rootDir`, or
100
100
  * names an item-builder registry this package does not ship.
101
101
  */
102
- export function configFromData(data: unknown, configPath: string): import("../config.mjs").ContentBuildConfig;
102
+ export function configFromData(data: unknown, configPath: string): import("../content-config.mjs").ContentBuildConfig;
103
103
  /**
104
104
  * The consuming repository's resolved, frozen configuration.
105
105
  *
@@ -108,12 +108,12 @@ export function configFromData(data: unknown, configPath: string): import("../co
108
108
  * configuration (#2). The result is memoised, so calling it in a default
109
109
  * parameter — the usual spelling here — costs one property read per call.
110
110
  *
111
- * @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
111
+ * @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
112
112
  * @throws {Error} When no configuration file can be found, or the one named
113
113
  * cannot be loaded. Absence is a defect, not a fallback: without it the
114
114
  * compilers know neither what to compile nor where to put it.
115
115
  */
116
- export function loadPackConfig(): import("../config.mjs").ContentBuildConfig;
116
+ export function loadPackConfig(): import("../content-config.mjs").ContentBuildConfig;
117
117
  /**
118
118
  * The file {@link loadPackConfig} resolved the configuration from.
119
119
  *
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The file name a downloaded book identifies itself by.
3
+ *
4
+ * The zip and the manifest take their names from the manifest, so an asset's
5
+ * name and its advertised URL cannot disagree. A PDF has no advertised URL, so
6
+ * its name is a free choice — which is exactly why it is fixed here rather than
7
+ * left for each of six consumers to invent.
8
+ *
9
+ * @param {string} artifact - The package's artifact name.
10
+ * @param {string} version - The version being released.
11
+ * @returns {string} `<artifact>-<version>.pdf`, or `<artifact>.pdf` unversioned.
12
+ */
13
+ export function pdfFileName(artifact: string, version: string): string;
14
+ /**
15
+ * Build the book.
16
+ *
17
+ * @param {object} [opts] - Options.
18
+ * @param {object} [opts.config] - A resolved configuration; loaded when absent.
19
+ * @param {string} [opts.out] - Where to write, overriding `pdf.out`.
20
+ * @param {string} [opts.version] - Stamped on the title page and the file name.
21
+ * @param {boolean} [opts.compile] - Whether to run Typst. False leaves the
22
+ * `.typ` source, which is what the unit tests read.
23
+ * @returns {Promise<object>} `{ built, reason, findings, typ, pdf, stats }`.
24
+ */
25
+ export function buildPdf({ config, out, version, compile }?: {
26
+ config?: object | undefined;
27
+ out?: string | undefined;
28
+ version?: string | undefined;
29
+ compile?: boolean | undefined;
30
+ }): Promise<object>;
31
+ /**
32
+ * Run Typst over the emitted source.
33
+ *
34
+ * @param {string} typPath - The `.typ` file.
35
+ * @param {string} pdfPath - Where the PDF goes.
36
+ * @param {object} pdf - The resolved `pdf:` block.
37
+ * @returns {{ok: boolean, message: string}} What happened.
38
+ */
39
+ export function compileTypst(typPath: string, pdfPath: string, pdf?: object): {
40
+ ok: boolean;
41
+ message: string;
42
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Every glyph name a font carries, with the codepoint that reaches it.
3
+ *
4
+ * @param {string} file - Path to a `.ttf`/`.otf`.
5
+ * @returns {Map<string, number>} Name → codepoint.
6
+ */
7
+ export function glyphTable(file: string): Map<string, number>;
8
+ /**
9
+ * The family name a font file announces, for Typst's `text(font: …)`.
10
+ *
11
+ * Read from the `name` table rather than from the file name, because the file
12
+ * is what a consumer happened to call it and the family is what the font stack
13
+ * will match on.
14
+ *
15
+ * @param {string} file - Path to a `.ttf`/`.otf`.
16
+ * @returns {string} The family name, or "" when the table cannot be read.
17
+ */
18
+ export function familyName(file: string): string;
19
+ /**
20
+ * Resolve every icon in a registry against the fonts a consumer named.
21
+ *
22
+ * @param {object} registry - The resolved `icons:` registry.
23
+ * @param {Record<string, string>} iconFonts - Family name → font file.
24
+ * @param {object[]} [findings] - Collected here rather than thrown.
25
+ * @returns {Map<string, {font: string, codepoint: number}>} Icon name → glyph.
26
+ */
27
+ export function resolveIconGlyphs(registry: object, iconFonts?: Record<string, string>, findings?: object[]): Map<string, {
28
+ font: string;
29
+ codepoint: number;
30
+ }>;
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Escape literal text for Typst markup.
3
+ *
4
+ * @param {string} text - Text as the author wrote it.
5
+ * @returns {string} The same text, inert.
6
+ */
7
+ export function escapeTypst(text: string): string;
8
+ /**
9
+ * Escape a string going inside Typst string quotes, as a `#link` URL does.
10
+ *
11
+ * @param {string} text - The raw value.
12
+ * @returns {string} The same value, quotable.
13
+ */
14
+ export function escapeTypstString(text: string): string;
15
+ /**
16
+ * A Typst label, from a plan anchor.
17
+ *
18
+ * Typst labels admit a narrower charset than an anchor does, so anything else
19
+ * folds to a hyphen. The plan already guarantees anchors are unique, and a fold
20
+ * that merged two of them would silently give one destination two meanings —
21
+ * so the fold is injective by construction: only characters Typst rejects move,
22
+ * and they move to a character the slugifier never emits twice in a row.
23
+ *
24
+ * @param {string} anchor - The plan's anchor.
25
+ * @returns {string} A Typst label name.
26
+ */
27
+ export function labelFor(anchor: string): string;
28
+ /**
29
+ * A markdown-it configured to parse, not to render.
30
+ *
31
+ * `html: false` is the load-bearing setting: raw HTML in a note has no route to
32
+ * Typst at all, which is why {@link module:engine/content-html} reports it. With
33
+ * HTML disabled markdown-it emits the tag as text, so it arrives in the book
34
+ * visibly wrong rather than invisibly missing.
35
+ *
36
+ * @param {object} [registry] - The icon registry.
37
+ * @returns {object} A markdown-it instance.
38
+ */
39
+ export function createParser(registry?: object): object;
40
+ /**
41
+ * Render markdown as Typst content.
42
+ *
43
+ * @param {string} markdown - The note's body, tables expanded and links resolved.
44
+ * @param {object} [opts] - Options.
45
+ * @param {object} [opts.md] - A parser from {@link createParser}, reused across
46
+ * a whole book rather than rebuilt for each of 2,500 notes.
47
+ * @param {object} [opts.registry] - The icon registry, when no parser is passed.
48
+ * @param {Map<string, string>} [opts.links] - Address slug → plan anchor.
49
+ * @param {Map<string, string>} [opts.glyphs] - Icon name → `{font, char}`.
50
+ * @param {number} [opts.headingOffset] - Added to every heading level, so a
51
+ * note's own `##` nests beneath the entry heading the book gave it.
52
+ * @param {string} [opts.anchorPrefix] - The entry's anchor, which namespaces
53
+ * every `{#slug}` the body declares.
54
+ * @returns {string} Typst markup.
55
+ */
56
+ export function markdownToTypst(markdown: string, opts?: {
57
+ md?: object | undefined;
58
+ registry?: object | undefined;
59
+ links?: Map<string, string> | undefined;
60
+ glyphs?: Map<string, string> | undefined;
61
+ headingOffset?: number | undefined;
62
+ anchorPrefix?: string | undefined;
63
+ }): string;
64
+ /**
65
+ * The whole book, as one Typst document.
66
+ *
67
+ * **Pure, and that is the point.** Everything a reviewer of #316 has to check
68
+ * about structure — the outline's shape, the anchors, which links went inward,
69
+ * the order entries print in — is decided here from a plan and a map of bodies,
70
+ * with no filesystem and no compiler. {@link module:engine/pdf-build} supplies
71
+ * both and runs Typst over the result.
72
+ *
73
+ * ## Three surfaces, one heading tree
74
+ *
75
+ * A roster of 2,500 entries wants every entry reachable from a viewer's
76
+ * sidebar, and emphatically does not want all 2,500 printed in the front
77
+ * matter: that is forty pages of contents before the book starts. It also
78
+ * wants every heading in a note's own body to keep working as a link target,
79
+ * without appearing on either surface — the anchor an author writes for
80
+ * `[[note#appearance]]` is structure, not a destination either outline offers
81
+ * on its own.
82
+ *
83
+ * `heading` carries `outlined` and `bookmarked` independently, so the three
84
+ * wants are three settings rather than three passes:
85
+ *
86
+ * - A **section** — `outlined: true, bookmarked: true` — prints in the paper
87
+ * contents and the PDF sidebar alike.
88
+ * - A **note leaf**, titled from `name.full`, is `outlined: false,
89
+ * bookmarked: true`: reachable from the sidebar, absent from the printed
90
+ * contents.
91
+ * - A **body heading**, inside a note's own markdown, is `outlined: false,
92
+ * bookmarked: false`: a real heading with a label, so it still supplies a
93
+ * link target, a running head and a page break, but neither outline lists
94
+ * it. {@link markdownToTypst} emits these.
95
+ *
96
+ * `#outline()` needs no depth limit under this model: what prints is decided
97
+ * per heading, not by how deep the tree happens to go.
98
+ *
99
+ * ## Headings carry the structure, so nothing else has to
100
+ *
101
+ * Every section, every prose file and every entry is a real Typst heading at
102
+ * its plan depth. That single decision supplies both outlines, the running
103
+ * heads and the page breaks at once — where drawing titles as styled text
104
+ * would have meant building all four by hand and keeping them agreeing with
105
+ * each other.
106
+ *
107
+ * @param {object} opts - Options.
108
+ * @param {object} opts.plan - From {@link module:engine/pdf-toc.planDocument}.
109
+ * @param {Map<string, string>} opts.bodies - Anchor → the entry's rendered
110
+ * Typst body. An entry with no body prints its heading alone.
111
+ * @param {string} opts.title - The document's title.
112
+ * @param {string} [opts.subtitle] - Shown under it on the title page.
113
+ * @param {string[]} [opts.front] - Rendered Typst for each front-matter file.
114
+ * @param {object} [opts.fonts] - `{ serif, sans, mono }` family names.
115
+ * @param {string} [opts.version] - Stamped on the title page when given.
116
+ * @returns {string} A complete `.typ` document.
117
+ */
118
+ export function renderBook({ plan, bodies, title, subtitle, front, fonts, version, }?: {
119
+ plan: object;
120
+ bodies: Map<string, string>;
121
+ title: string;
122
+ subtitle?: string | undefined;
123
+ front?: string[] | undefined;
124
+ fonts?: object | undefined;
125
+ version?: string | undefined;
126
+ }): string;
127
+ /**
128
+ * Point every internal link at a label the document actually declares.
129
+ *
130
+ * **Typst refuses to compile a reference to a label that is not there.** That
131
+ * makes one mistyped `[[note#appearance]]`, or an anchor written inside a code
132
+ * fence where no heading is emitted, fatal to a 1,200-page book — and fatal at
133
+ * the very end, after everything else has succeeded. A reference book cannot
134
+ * have that failure mode: the link is the least important thing on the page and
135
+ * would be taking the other two thousand entries down with it.
136
+ *
137
+ * So references are reconciled against declarations before the source is
138
+ * written. A link to a section that does not exist falls back to the **entry**
139
+ * that would have contained it, which is where a reader wants to end up anyway;
140
+ * a link with no entry to fall back to becomes plain text. Both are reported.
141
+ *
142
+ * A declaration is a label not preceded by `#link(` — the only two places a
143
+ * label appears are the heading that declares one and the link that uses one.
144
+ *
145
+ * @param {string} source - The assembled Typst document.
146
+ * @param {object[]} [findings] - Collected here rather than thrown.
147
+ * @returns {string} The same document, with no reference left dangling.
148
+ */
149
+ export function resolveDanglingLabels(source: string, findings?: object[]): string;
150
+ /**
151
+ * Every icon name a body uses, so a build can resolve them once.
152
+ *
153
+ * @param {string} markdown - A note body.
154
+ * @returns {string[]} The names, in order of appearance, with repeats.
155
+ */
156
+ export function iconNamesIn(markdown: string): string[];
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Validate the raw tree and flatten it into nodes.
3
+ *
4
+ * The format has no chapter/section distinction: every node is a section, and
5
+ * the leaves of the *document* are the notes and prose its contents resolve to.
6
+ * Depth is whatever the tree says, so nothing here caps or normalises it.
7
+ *
8
+ * `contents` is an **ordered, heterogeneous** list — prose, filters and child
9
+ * sections interleave in the sequence written, and that sequence is preserved
10
+ * exactly. A section that opens with a `file:` and then lists its entries is
11
+ * saying something different from one that does the reverse.
12
+ *
13
+ * @param {unknown} raw - The parsed document, as YAML returns it.
14
+ * @param {object} [opts] - Options.
15
+ * @param {string} [opts.text] - The file's source, for finding positions. Every
16
+ * finding without one is still reported, just without a locator.
17
+ * @returns {{nodes: object[], findings: object[]}} The flattened tree and what
18
+ * was wrong with it.
19
+ */
20
+ export function parseDocumentTree(raw: unknown, { text }?: {
21
+ text?: string | undefined;
22
+ }): {
23
+ nodes: object[];
24
+ findings: object[];
25
+ };
26
+ /**
27
+ * Run every filter, and report the ones that would not run.
28
+ *
29
+ * The I/O half, kept apart from the planner for the reason the whole engine
30
+ * keeps them apart: the plan is then assertable without a database. The build
31
+ * owns the statement — `SELECT * FROM notes WHERE <clause>` — which is what
32
+ * makes a filter unable to reach another package's schema, unable to project
33
+ * something that is not a note, and unable to pick up the `doc<type>`
34
+ * documentation rows that ride the same index as the notes they document.
35
+ *
36
+ * **A filter that selects nothing is an error**, and the distinction that makes
37
+ * that consistent is worth stating. A *note* no clause selects is expected: the
38
+ * book is a selection and a project decides what its own volume carries. A
39
+ * *clause* that selects no note is not the same thing — a filter is a deliberate
40
+ * act, so one matching nothing is either wrong or left over from a structure
41
+ * that has moved on, and in both cases the tree should not carry it. Reported
42
+ * with the section's name and the filter's position, so the choice between
43
+ * fixing it and deleting it is the author's.
44
+ *
45
+ * @param {object[]} nodes - From {@link parseDocumentTree}.
46
+ * @param {{query: (sql: string) => Promise<{rows: object[]}>}} db - An open
47
+ * database, from {@link module:engine/sql-tables.openNotesDatabase}.
48
+ * @param {object} [opts] - Options.
49
+ * @param {(record: object) => boolean} [opts.keep] - Which rows are notes.
50
+ * @param {string} [opts.text] - The document's source, for finding positions.
51
+ * @returns {Promise<{selections: Map<string, object[]>, findings: object[]}>}
52
+ */
53
+ export function runTreeFilters(nodes: object[], db: {
54
+ query: (sql: string) => Promise<{
55
+ rows: object[];
56
+ }>;
57
+ }, { keep, text }?: {
58
+ keep?: ((record: object) => boolean) | undefined;
59
+ text?: string | undefined;
60
+ }): Promise<{
61
+ selections: Map<string, object[]>;
62
+ findings: object[];
63
+ }>;
64
+ /**
65
+ * Resolve the flattened tree into the document plan.
66
+ *
67
+ * The plan is an ordered list of entries — `section`, `prose`, `note` — and it
68
+ * is the artifact worth having. Order, depth, the outline, the table of
69
+ * contents, anchor uniqueness and every link destination are all readable from
70
+ * it, so nearly the whole of what #316 asks for can be asserted here, on data,
71
+ * without a renderer or a PDF. Only how the result *looks* needs eyes.
72
+ *
73
+ * **Notes are sorted, prose is not.** A section's entries come out in
74
+ * `nameAscii` order — the ASCII fold `buildIndexRecord` already derives, so a
75
+ * circumflex sorts with its letter instead of after `Z` as a raw codepoint
76
+ * comparison would put it. Prose keeps the position it was written in, because
77
+ * its place in the sequence is the author's statement.
78
+ *
79
+ * **A section with nothing in it does not print**, and emptiness is judged after
80
+ * its descendants are: a section holding only sections that all resolved to
81
+ * nothing is itself empty. In a correct tree this never fires — a filter that
82
+ * selects nothing is reported by {@link runTreeFilters} as the error it is — so
83
+ * this is the graceful half of that failure rather than a feature: a build whose
84
+ * filters are broken still produces a readable document to look at while they
85
+ * are fixed. A section holding only prose is not empty; it is a preface.
86
+ *
87
+ * @param {object[]} nodes - From {@link parseDocumentTree}.
88
+ * @param {object} [opts] - Options.
89
+ * @param {Map<string, object[]>} [opts.selections] - Records each filter
90
+ * selected, keyed by item id. Run by the caller: DuckDB is async and this is
91
+ * not.
92
+ * @returns {{entries: object[], links: Map<string, string>, stats: object}}
93
+ * The plan, the address→anchor map inbound wikilinks resolve through, and
94
+ * what the selection came to.
95
+ */
96
+ export function planDocument(nodes: object[], { selections }?: {
97
+ selections?: Map<string, object[]> | undefined;
98
+ }): {
99
+ entries: object[];
100
+ links: Map<string, string>;
101
+ stats: object;
102
+ };
103
+ /**
104
+ * Presentation a node may declare, and that its descendants inherit.
105
+ *
106
+ * Reserved now although the first release renders none of them, because the
107
+ * shape of the file is the thing consumers commit to: a book that has to be
108
+ * restructured to gain a running head has the wrong format, not the wrong
109
+ * renderer. Inheritance is what makes them worth declaring at all — `Gear` says
110
+ * once which infobox its entries use, and nine sections beneath it agree.
111
+ *
112
+ * @type {readonly string[]}
113
+ */
114
+ export const PRESENTATION_KEYS: readonly string[];
@@ -13,6 +13,15 @@
13
13
  * @returns {Set<string>} The known action names.
14
14
  */
15
15
  export function collectKnownActionNames(repoRoot: string): Set<string>;
16
+ /**
17
+ * Scenes pack compiler.
18
+ *
19
+ * Walks the content tree and compiles every map note into one Scene, resolving
20
+ * what one note says about another through an index built before any scene is
21
+ * written. It also writes one Adventure per place, bundling those scenes with
22
+ * the JournalEntries their prose compiled into, which is what makes a pinned
23
+ * scene's id-based references resolve on import.
24
+ */
16
25
  export class Scenes extends BasePackCompiler {
17
26
  constructor({ contentBase, dest, skipDirectories, companionDests, folderResolver, repoRoot, }: {
18
27
  contentBase: any;
@@ -31,7 +40,7 @@ export class Scenes extends BasePackCompiler {
31
40
  */
32
41
  adventureCount: number;
33
42
  index: Map<string, object> | undefined;
34
- effectsByAddress: Map<any, any> | undefined;
43
+ effectsByAddress: Map<string, object> | undefined;
35
44
  knownActions: Set<string> | undefined;
36
45
  /** place key → `{name, img, scenes: [], journal: []}` */
37
46
  places: Map<any, any> | undefined;
@@ -291,7 +291,7 @@ export function checkAuthoredSystemData(fm: object, { block, documentType, subTy
291
291
  * existed, or a subtype the artifact does not name, produces no findings.
292
292
  * `content-build lint` is where a missing artifact is said out loud, once.
293
293
  *
294
- * @param {object} system - The `system` block the compiler produced.
294
+ * @param {object} emitted - The `system` block the compiler produced.
295
295
  * @param {object} opts
296
296
  * @param {object} opts.fm - The note's frontmatter, for the authored paths this
297
297
  * check leaves to {@link checkAuthoredSystemData}.
@@ -307,7 +307,7 @@ export function checkAuthoredSystemData(fm: object, { block, documentType, subTy
307
307
  * @param {object} [opts.config] - The resolved build configuration.
308
308
  * @returns {(EmissionFinding & {message: string})[]} One per undeclared path.
309
309
  */
310
- export function checkEmittedSystemData(emitted: any, { fm, block, documentType, subType, type, fields, system, config, }: {
310
+ export function checkEmittedSystemData(emitted: object, { fm, block, documentType, subType, type, fields, system, config, }: {
311
311
  fm: object;
312
312
  block: string;
313
313
  documentType: string;
@@ -126,10 +126,13 @@ export function writeHomepages(outRoot: string, pages: readonly object[], config
126
126
  * @param {object[]} pages - Every page, from both walks.
127
127
  * @param {object} findings - `{ addressFindings, fmLinkFindings }` from
128
128
  * collection.
129
- * @param {object} options - `{ config }`.
129
+ * @param {object} options
130
+ * @param {object} options.config - The resolved build configuration.
130
131
  * @returns {object} The gate results and, when they pass, the built index.
131
132
  */
132
- export function siteGates(pages: object[], findings: object, { config }: object): object;
133
+ export function siteGates(pages: object[], findings: object, { config }: {
134
+ config: object;
135
+ }): object;
133
136
  /**
134
137
  * The gate result of a build that ran none of them.
135
138
  *
@@ -220,10 +223,18 @@ export function sectionFrontmatter(meta: object): object;
220
223
  * section's landing and takes the title and hero the section declares.
221
224
  *
222
225
  * @param {object} page - The page.
223
- * @param {object} options - `{ readmeSections, decorate }`.
226
+ * @param {object} options
227
+ * @param {Record<string, object>} [options.readmeSections] - The sections a
228
+ * published tree declares, which a tree page's own `README` is the landing
229
+ * for.
230
+ * @param {(data: object, page: object) => void} [options.decorate] - Called
231
+ * with each page's frontmatter, for whatever a consumer's own pass adds.
224
232
  * @returns {object} The frontmatter to write.
225
233
  */
226
- export function pageFrontmatter(page: object, { readmeSections, decorate }: object): object;
234
+ export function pageFrontmatter(page: object, { readmeSections, decorate }: {
235
+ readmeSections?: Record<string, object> | undefined;
236
+ decorate?: ((data: object, page: object) => void) | undefined;
237
+ }): object;
227
238
  /**
228
239
  * Where a page is written, relative to the output root.
229
240
  *
@@ -310,10 +321,21 @@ export function renderPages(pages: object[], options: object): {
310
321
  * navigation of every page inside it.
311
322
  *
312
323
  * @param {string} outRoot - The mount directory.
313
- * @param {object} options - `{ sections, landing, sectionTitle }`.
324
+ * @param {object} options
325
+ * @param {Record<string, object>} [options.sections] - The declared sections,
326
+ * each written as a titled `_index.md` carrying its own frontmatter.
327
+ * @param {object} [options.landing] - The mount's own landing frontmatter.
328
+ * Omitted, the mount gets no `_index.md` of its own.
329
+ * @param {((name: string) => string)|null} [options.sectionTitle] - Titles a
330
+ * directory below the mount that declared no section. `null` leaves such a
331
+ * directory without an `_index.md`.
314
332
  * @returns {number} How many landings were written.
315
333
  */
316
- export function writeSectionLandings(outRoot: string, { sections, landing, sectionTitle }: object): number;
334
+ export function writeSectionLandings(outRoot: string, { sections, landing, sectionTitle }: {
335
+ sections?: Record<string, object> | undefined;
336
+ landing?: object | undefined;
337
+ sectionTitle?: ((name: string) => string) | null | undefined;
338
+ }): number;
317
339
  /**
318
340
  * A section landing's title, from its directory name — `macro` → `Macros`.
319
341
  *
@@ -370,12 +392,18 @@ export function resolveOutputRoot(rootDir: string, out: string): string;
370
392
  * @param {object} [options.config] - A resolved configuration; loaded when
371
393
  * omitted.
372
394
  * @param {string} [options.outRoot] - Override the configured output mount.
395
+ * @param {Map<string, object[]>} [options.sqlTables] - Prepared `sql` results,
396
+ * keyed by the note's absolute file, from
397
+ * {@link module:engine/sql-tables.prepareSqlTables}. A page authoring an
398
+ * `sql` directive with none prepared is a table error: nothing here runs a
399
+ * query.
373
400
  * @returns {{gates: object, stats: object|null, tableErrors: object[],
374
401
  * wikiErrors: object[], manifests: object|null}}
375
402
  */
376
403
  export function buildSite({ config, outRoot, sqlTables }?: {
377
404
  config?: object | undefined;
378
405
  outRoot?: string | undefined;
406
+ sqlTables?: Map<string, object[]> | undefined;
379
407
  }): {
380
408
  gates: object;
381
409
  stats: object | null;
@@ -55,8 +55,8 @@ export function resolveItemDocType(qualifier: string, types: Set<string>): strin
55
55
  *
56
56
  * **Parsing is plain positional counting**, the same rule
57
57
  * {@link readCanonicalKey} follows, and it is sound for the same reason: every
58
- * segment is `^[A-Za-z0-9]+$` (`ADDRESS_SEGMENT_PATTERN`, enforced on
59
- * shortcodes by `content-lint.mjs`), so the hyphen is purely a
58
+ * segment matches `ADDRESS_SEGMENT_PATTERN` (enforced on shortcodes by
59
+ * `content-lint.mjs`), so the hyphen is purely a
60
60
  * separator and the count alone determines every field. Verified across the
61
61
  * four content trees: 138,204 authored shortcodes, none carrying a separator.
62
62
  *
@@ -144,7 +144,6 @@ export function buildWikilinkIndex(docs: Array<{
144
144
  byShortcode: Map<string, object>;
145
145
  types: Set<string>;
146
146
  };
147
- /** Matches a whole wikilink, capturing its inner text. */
148
147
  /**
149
148
  * Rewrites every wikilink in a markdown body as a Foundry UUID enricher.
150
149
  *
@@ -1,3 +1,11 @@
1
+ /**
2
+ * HM3's Actor compile pass.
3
+ *
4
+ * Declares HM3's note-type → document-subtype map and builds a `being` note's
5
+ * document: the subtype the note authors in `hm3.type`, the mapped fields and
6
+ * the two anchored prose sections, and the embedded items `hm3.items` names.
7
+ * Everything else is {@link module:engine/actor-compiler}'s.
8
+ */
1
9
  export class Hm3Actors extends SystemActorCompiler {
2
10
  /**
3
11
  * HM3's note-type → document-subtype map — the one declaration that says
@@ -1,3 +1,11 @@
1
+ /**
2
+ * HM3's Item compile pass.
3
+ *
4
+ * Declares HM3's note-type → document-subtype map, which decides the notes this
5
+ * pass claims and what each becomes, the one `system` key HM3 writes from prose,
6
+ * and the template-priority flag HM3's data model has no field for. Everything
7
+ * else is {@link module:engine/item-compiler}'s.
8
+ */
1
9
  export class Hm3Items extends SystemItemCompiler {
2
10
  /**
3
11
  * HM3's note-type → document-subtype map — the one declaration that says
@@ -15,22 +15,33 @@
15
15
  * Determines both asset names.
16
16
  * @param {string} [opts.metadataDir] - Where the build writes its content
17
17
  * index, consulted when the advertised file was not staged.
18
+ * @param {boolean} [opts.pdf] - Whether to build the book that ships beside the
19
+ * archive. `true` by default; `false` skips the build and reports the skip.
18
20
  * @returns {Promise<{zip: string, manifest: string, metadata?: string,
19
- * bytes: number, version: string}>} The paths written, the archive's size,
20
- * and the version the manifest declares. `metadata` is absent when the
21
- * manifest advertises no content index.
21
+ * pdf?: string, pdfFindings: object[], pdfSkipped: string|null,
22
+ * bytes: number, version: string}>} The paths written, what the book build
23
+ * found, the archive's size, and the version the manifest declares.
24
+ * `metadata` is absent when the manifest advertises no content index, and
25
+ * `pdf` is absent when no book was written. `pdfSkipped` is `null` when a
26
+ * book was built, and otherwise the reason none was — itself `null` when the
27
+ * book builder could not be loaded, which is reported through
28
+ * `pdfFindings`.
22
29
  * @throws {Error} When the stage has no manifest — there is nothing to release,
23
30
  * and an archive without one installs as nothing.
24
31
  */
25
- export function packRelease({ stageDir, outDir, artifact, metadataDir, }?: {
32
+ export function packRelease({ stageDir, outDir, artifact, metadataDir, pdf, }?: {
26
33
  stageDir?: string | undefined;
27
34
  outDir?: string | undefined;
28
35
  artifact?: "module" | "system" | undefined;
29
36
  metadataDir?: string | undefined;
37
+ pdf?: boolean | undefined;
30
38
  }): Promise<{
31
39
  zip: string;
32
40
  manifest: string;
33
41
  metadata?: string;
42
+ pdf?: string;
43
+ pdfFindings: object[];
44
+ pdfSkipped: string | null;
34
45
  bytes: number;
35
46
  version: string;
36
47
  }>;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * SoHL's Actor compile pass.
3
+ *
4
+ * Declares SoHL's note-type → document-subtype map and builds a `being` note's
5
+ * document: the body structure and its movement profiles, the embedded
6
+ * attribute and item documents the frontmatter names, and the `system` block.
7
+ * Everything else is {@link module:engine/actor-compiler}'s.
8
+ */
1
9
  export class Actors extends SystemActorCompiler {
2
10
  /**
3
11
  * SoHL's note-type → document-subtype map — the one declaration that says
@@ -12,7 +20,7 @@ export class Actors extends SystemActorCompiler {
12
20
  * Build all embedded items for an actor: one per `sohl.attributes`
13
21
  * entry plus one per `sohl.items` entry. `sohl.skills` is ignored.
14
22
  */
15
- buildEmbeddedItems(itemsMap: any, actorId: any, fm: any, ctx: any): any[];
23
+ buildEmbeddedItems(itemsMap: any, actorId: any, fm: any, ctx: any): object[];
16
24
  /**
17
25
  * Bake each unopened skill's opening mastery level into the document.
18
26
  *
@@ -52,7 +60,7 @@ export class Actors extends SystemActorCompiler {
52
60
  appearance: string;
53
61
  dossier: string;
54
62
  };
55
- items: any[];
63
+ items: object[];
56
64
  prototypeToken: {
57
65
  name: any;
58
66
  displayName: number;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * SoHL's Item compile pass.
3
+ *
4
+ * Declares SoHL's note-type → document-subtype map, which decides the notes
5
+ * this pass claims and what each becomes, and the `system` keys SoHL writes on
6
+ * every item whatever its type. Everything else is
7
+ * {@link module:engine/item-compiler}'s.
8
+ */
1
9
  export class Items extends SystemItemCompiler {
2
10
  /**
3
11
  * SoHL's note-type → document-subtype map — the one declaration that says