@heroiclands/package-build 23.7.1 → 23.9.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 +41 -0
- package/README.md +32 -0
- package/bin/content-commands.mjs +32 -10
- package/ci/check-banners.mjs +37 -19
- package/container.mjs +48 -10
- package/docs/api.md +402 -58
- package/docs/authoring/first-note.md +1 -1
- package/docs/authoring/frontmatter.md +30 -1
- package/docs/authoring/links-and-markup.md +154 -56
- package/docs/commands.md +31 -12
- package/docs/content-format.md +25 -2
- package/docs/design.md +31 -0
- package/docs/diagnostics.md +1 -1
- package/docs/guides/e2e.md +37 -1
- package/docs/guides/packs.md +2 -1
- package/docs/guides/reference-pages.md +86 -63
- package/docs/project-setup.md +30 -15
- package/docs/reference/format-details.md +313 -132
- package/docs/reference/note-types.md +191 -174
- package/e2e.mjs +89 -2
- package/engine/anchors.mjs +7 -20
- package/engine/base-compiler.mjs +11 -18
- package/engine/compile-corpus.mjs +5 -3
- package/engine/content-alerts.mjs +4 -2
- package/engine/content-blocks.mjs +18 -36
- package/engine/content-format.yaml +1 -0
- package/engine/content-index.mjs +447 -33
- package/engine/content-links.mjs +43 -15
- package/engine/content-poetry.mjs +9 -0
- package/engine/content-tables.mjs +8 -4
- package/engine/culture-reach.mjs +61 -34
- package/engine/data-keys.mjs +3 -1
- package/engine/diagnostics.mjs +24 -11
- package/engine/dictionary-layout.mjs +151 -19
- package/engine/dictionary-rows.mjs +129 -79
- package/engine/foundry-entries.mjs +21 -3
- package/engine/frontmatter-lint.mjs +1 -1
- package/engine/generate.mjs +12 -22
- package/engine/glossary-entries.mjs +11 -11
- package/engine/heading-attributes.mjs +21 -28
- package/engine/helpers.mjs +20 -12
- package/engine/infobox.mjs +3 -0
- package/engine/journals.mjs +15 -2
- package/engine/language-data.mjs +53 -0
- package/engine/language-terms.mjs +86 -0
- package/engine/language-words.mjs +2 -2
- package/engine/lexical-sections.mjs +247 -0
- package/engine/markdownlint-run.mjs +12 -2
- package/engine/markup-validation.mjs +34 -0
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-claims.mjs +21 -10
- package/engine/note-package.mjs +3 -5
- package/engine/note-state.mjs +9 -3
- package/engine/note-vocabulary.mjs +36 -7
- package/engine/pdf-build.mjs +17 -10
- package/engine/prose-lint.mjs +12 -6
- package/engine/related-pages.mjs +22 -3
- package/engine/see-also.mjs +150 -0
- package/engine/site-build.mjs +21 -3
- package/engine/site-preview.mjs +4 -2
- package/engine/sql-tables.mjs +287 -53
- package/engine/standings.mjs +14 -12
- package/engine/stub-lint.mjs +6 -2
- package/engine/web-wikilinks.mjs +8 -6
- package/engine/word-rows.mjs +20 -12
- package/engine/word-terms.mjs +15 -6
- package/hugo-theme/layouts/partials/related.html +25 -8
- package/package.json +2 -1
- package/types/container.d.mts +4 -3
- package/types/e2e.d.mts +28 -0
- package/types/engine/base-compiler.d.mts +2 -2
- package/types/engine/compile-corpus.d.mts +2 -1
- package/types/engine/content-index.d.mts +85 -8
- package/types/engine/content-tables.d.mts +2 -1
- package/types/engine/culture-reach.d.mts +22 -2
- package/types/engine/data-keys.d.mts +7 -0
- package/types/engine/diagnostics.d.mts +7 -6
- package/types/engine/dictionary-layout.d.mts +35 -4
- package/types/engine/dictionary-rows.d.mts +9 -0
- package/types/engine/foundry-entries.d.mts +3 -1
- package/types/engine/heading-attributes.d.mts +2 -2
- package/types/engine/helpers.d.mts +11 -5
- package/types/engine/language-terms.d.mts +7 -0
- package/types/engine/lexical-sections.d.mts +95 -0
- package/types/engine/markdownlint-run.d.mts +3 -1
- package/types/engine/markup-validation.d.mts +14 -0
- package/types/engine/note-addresses.d.mts +10 -0
- package/types/engine/note-claims.d.mts +7 -1
- package/types/engine/note-package.d.mts +3 -1
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/note-vocabulary.d.mts +1 -0
- package/types/engine/related-pages.d.mts +10 -1
- package/types/engine/see-also.d.mts +55 -0
- package/types/engine/site-build.d.mts +4 -1
- package/types/engine/sql-tables.d.mts +40 -6
- package/types/engine/word-rows.d.mts +9 -7
package/docs/api.md
CHANGED
|
@@ -348,6 +348,20 @@ An addressed content note accepts exactly these top-level keys, in this order: `
|
|
|
348
348
|
| `dataFields` | `dataFields(type, vocabulary)` | `readonly DataFieldSpec[] \| undefined` | looking up the `data:` keys a note type may carry |
|
|
349
349
|
| `subTypes` | `subTypes(type, vocabulary)` | `readonly string[] \| null \| undefined` | looking up the closed `subType` values a note type declares |
|
|
350
350
|
|
|
351
|
+
### `./engine/see-also`
|
|
352
|
+
|
|
353
|
+
Import `@heroiclands/package-build/engine/see-also` for the deliberate
|
|
354
|
+
cross-references declared by `data.seeAlso`. Entries preserve authored order;
|
|
355
|
+
the caller supplies the medium's reference resolver and link renderer.
|
|
356
|
+
|
|
357
|
+
| Export | Signature | Returns | Use it when |
|
|
358
|
+
| ----------------- | ----------------------------------------------- | ----------------------------- | ------------------------------------------------------------------- |
|
|
359
|
+
| `checkSeeAlso` | `checkSeeAlso(note, { index, addressContext })` | `object[]` | locating malformed, unresolved, duplicate or self references |
|
|
360
|
+
| `seeAlsoEntries` | `seeAlsoEntries(fm, resolve)` | `object[]` | resolving names and destinations with optional glosses |
|
|
361
|
+
| `seeAlsoMarkdown` | `seeAlsoMarkdown(entries, link, level = 2)` | `string` | emitting an ordered See also section, empty when no entries resolve |
|
|
362
|
+
| `seeAlsoWebLink` | `seeAlsoWebLink(entry)` | `string` | writing a resolved book reference as a Markdown link |
|
|
363
|
+
| `seeAlsoPages` | `seeAlsoPages(pages, resolve)` | `Map<string, RelatedEntry[]>` | deriving ordered site connections for both builds and live previews |
|
|
364
|
+
|
|
351
365
|
### `engine.infobox`
|
|
352
366
|
|
|
353
367
|
The declared infobox: what a note's summary panel holds, decided once and rendered by each medium. The note box's fields are the type's own `data:` vocabulary, in its declared order, so a key added to a type appears everywhere with no second edit; the overlay declares only the label and the handful of keys that carry no row.
|
|
@@ -412,7 +426,14 @@ Drawing a declared infobox: HTML for separate Foundry pages appended after autho
|
|
|
412
426
|
|
|
413
427
|
### `engine.noteState`
|
|
414
428
|
|
|
415
|
-
The three states of a note — **stub**, **draft**, **full** —
|
|
429
|
+
The three states of a note — **stub**, **draft**, **full** — describe its page
|
|
430
|
+
content. A stubbable note with an empty body and no generated Dictionary or
|
|
431
|
+
Glossary content is a stub: its facts remain queryable in the index, but it
|
|
432
|
+
has no page, address or link target. `isStubNote` recognizes nonempty lexical
|
|
433
|
+
frontmatter as generated content, so collectors retain the resulting page.
|
|
434
|
+
That lexical frontmatter supplies content without an authored body.
|
|
435
|
+
The ladder is derived in the SQL `entries` view; a note cannot assert its own
|
|
436
|
+
state.
|
|
416
437
|
|
|
417
438
|
| Export | Signature | Returns | Use it when |
|
|
418
439
|
| --------------------- | ------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -444,7 +465,7 @@ What a draft note says about itself, in one sentence, on every surface it reache
|
|
|
444
465
|
|
|
445
466
|
### `engine.stubLint`
|
|
446
467
|
|
|
447
|
-
Every typed note must have a nonempty body
|
|
468
|
+
Nonempty `data.words` on a `skill`/`language` note, or `data.glossary` on a culture or affiliation, supplies content without an authored body. Absent or empty arrays supply no such exemption. Every other typed note must have a nonempty body: empty and whitespace-only bodies are errors regardless of description or `draft` tag, including `folder` and `homepage`. Nonempty placeholder-only bodies still produce errors, including on lexical notes. Nonempty bodies with fewer than 25 prose words produce warnings unless tagged `draft`. A `folder` note is held only to the nonempty rule: any body, placeholder or short, passes with or without `draft`. Untyped vault scaffolding is excluded. The lint, content-index, package compile, site and PDF commands enforce body errors before publishing.
|
|
448
469
|
|
|
449
470
|
| Export | Signature | Returns | Use it when |
|
|
450
471
|
| ------------------ | ---------------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
@@ -847,32 +868,87 @@ The published content index — the artifact packages exchange addresses through
|
|
|
847
868
|
|
|
848
869
|
Emitting this package's cross-package link manifest. `engine/content-address.mjs` owns the address _grammar_ — how a key is version is read, how a foreign file resolves. This module owns the _pass_: walking a content tree and deriving, for every note it publishes, the addresses that entry states. The two halves were split across the format module and a hand-written script in each consuming repository, which is how the two scripts came to differ in ways nobody chose — one routes its UUIDs through the pack router and one does not, and neither knew.
|
|
849
870
|
|
|
850
|
-
| Export | Signature | Returns | Use it when
|
|
851
|
-
| ----------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
852
|
-
| `LEAD_ANCHOR` | `const LEAD_ANCHOR` | — | The reserved anchor name for a journal's **first** page.
|
|
853
|
-
| `anchorsOf` | `function anchorsOf(entryUuid, entryId, body, name)` | {Record<string, string>} The anchors. | Every
|
|
854
|
-
| `entriesForNote` | `function entriesForNote(fm, name, address, body, ctx)` | {Array<object>} One or two entries, in {@link buildManifest}'s shape. | The manifest entries a single note produces.
|
|
855
|
-
| `collectFoundryEntries` | `function collectFoundryEntries(contentBase, ctx)` | `{{entries: Array<object>, notes: number, skipped: Array<{file: string, reason: string}>}}` | Every note this package publishes, as manifest entries.
|
|
856
|
-
| `foundryIdentities` | `function foundryIdentities(config` | `{{contentPackage: string, foundryPackageId: string, packRouter: object, web: boolean, skipDirectories: readonly string[]}}` | The identities an emission runs against, from configuration.
|
|
857
|
-
| `entryContext` | `function entryContext(config` | `{{contentPackage: string, foundryPackageId: string, packRouter: object, web: boolean, skipDirectories: readonly string[]}}` | The identities an emission runs against, from configuration.
|
|
871
|
+
| Export | Signature | Returns | Use it when |
|
|
872
|
+
| ----------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
873
|
+
| `LEAD_ANCHOR` | `const LEAD_ANCHOR` | — | The reserved anchor name for a journal's **first** page. |
|
|
874
|
+
| `anchorsOf` | `function anchorsOf(entryUuid, entryId, body, name)` | {Record<string, string>} The anchors. | Every anchor of a note's journal, as `anchorName → Foundry target`. |
|
|
875
|
+
| `entriesForNote` | `function entriesForNote(fm, name, address, body, ctx)` | {Array<object>} One or two entries, in {@link buildManifest}'s shape. | The manifest entries a single note produces. |
|
|
876
|
+
| `collectFoundryEntries` | `function collectFoundryEntries(contentBase, ctx)` | `{{entries: Array<object>, notes: number, skipped: Array<{file: string, reason: string}>}}` | Every note this package publishes, as manifest entries. |
|
|
877
|
+
| `foundryIdentities` | `function foundryIdentities(config` | `{{contentPackage: string, foundryPackageId: string, packRouter: object, web: boolean, skipDirectories: readonly string[]}}` | The identities an emission runs against, from configuration. |
|
|
878
|
+
| `entryContext` | `function entryContext(config` | `{{contentPackage: string, foundryPackageId: string, packRouter: object, web: boolean, skipDirectories: readonly string[]}}` | The identities an emission runs against, from configuration. |
|
|
879
|
+
|
|
880
|
+
A generated dictionary headword maps to its containing page's UUID followed
|
|
881
|
+
by its word fragment. Other authored spans keep their containing-page
|
|
882
|
+
mapping.
|
|
858
883
|
|
|
859
884
|
### `engine.contentIndex`
|
|
860
885
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
| Export | Signature
|
|
864
|
-
| ----------------------- |
|
|
865
|
-
| `noteAddress` | `function noteAddress(frontmatter, contentPackage)`
|
|
866
|
-
| `sortKeysDeep` | `function sortKeysDeep(value)`
|
|
867
|
-
| `asciiName` | `function asciiName(name)`
|
|
868
|
-
| `asciiAliases` | `function asciiAliases(aliases)`
|
|
869
|
-
| `serializeContentIndex` | `function serializeContentIndex(records)`
|
|
870
|
-
| `indexRecordsFor` | `indexRecordsFor({ contentBase, config, skipDirectories, problems })` | {object[]} One record per note, plus one per documentation entry.
|
|
871
|
-
| `indexRecordsForNote` | `indexRecordsForNote({ frontmatter, body, bodyLine, relPath, absPath, contentPackage, manifest, addressContext })`
|
|
872
|
-
| `
|
|
873
|
-
| `
|
|
874
|
-
| `
|
|
875
|
-
| `
|
|
886
|
+
Collecting and publishing this package's content index. Records describe note frontmatter, assets, prose and event anchors, and the document targets those notes produce. Dynamic publication prepares the same generated Markdown and scalar expressions as the rendering surfaces, using each source note's audience.
|
|
887
|
+
|
|
888
|
+
| Export | Signature | Returns | Use it when |
|
|
889
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
890
|
+
| `noteAddress` | `function noteAddress(frontmatter, contentPackage)` | {{slug: string, canonical: string}\|null} `slug` is what goes inside `[[…]]` within this package; `canonical` is the fully qualified key the manifest files the note under, carrying the package and the system as well. | The address a wikilink writes to reach a note, or `null` when it has none. |
|
|
891
|
+
| `sortKeysDeep` | `function sortKeysDeep(value)` | {unknown} The value with every plain object's keys in sorted order. | Recursively sort an object's keys, so serialization is order-independent. |
|
|
892
|
+
| `asciiName` | `function asciiName(name)` | {string\|null} The ASCII form, or `null` when there is no name, or nothing printable survives. | A note's display name reduced to printable 7-bit ASCII. |
|
|
893
|
+
| `asciiAliases` | `function asciiAliases(aliases)` | {Array<string>} Possibly empty, never null: a note with no aliases has an empty set of them, which is a fact rather than a missing value, and a consumer iterating it should not have to check first. | A note's `name.aliases` reduced to printable 7-bit ASCII, in order. |
|
|
894
|
+
| `serializeContentIndex` | `function serializeContentIndex(records)` | {string} One compact JSON object per line, newline-terminated. | Serialize records as JSON Lines. |
|
|
895
|
+
| `indexRecordsFor` | `indexRecordsFor({ contentBase, config, skipDirectories, problems, parsed })` | {object[]} One record per note, plus one per documentation entry. | The index records for a content tree, without writing anything. |
|
|
896
|
+
| `indexRecordsForNote` | `indexRecordsForNote({ frontmatter, body, bodyLine, relPath, absPath, raw, contentPackage, manifest, addressContext })` | `object[]` | Derive the note's own record and its documentation record from parsed text. |
|
|
897
|
+
| `prepareContentIndex` | `async prepareContentIndex({ contentBase, config } = {})` | `Promise<PreparedContentIndex>` — the captured source context and complete expanded bodies, described below | Prepare dynamic publication without writing files. |
|
|
898
|
+
| `emitContentIndex` | `emitContentIndex({ contentBase, outDir, config, publication } = {})` | `{{file: string, notes: number, assets: number, records: number, bytes: number, full: number, draft: number, stub: number, dictionary: {file: string, rows: number}, terms: {file: string, rows: number}}}` | Synchronously publish metadata and the dictionary and terms datafiles; dynamic content requires a complete `publication`. |
|
|
899
|
+
| `buildIndexRecord` | `buildIndexRecord({ frontmatter, relPath, absPath, raw, contentPackage, body, bodyLine, manifest })` | `Record<string, any>` — the record, with derived fields sorted deep | building one note's content-index record; refuses a note that authors a key the index derives itself (`package` among them) before building anything |
|
|
900
|
+
| `collectContentIndex` | `collectContentIndex(contentBase, { contentPackage, skipDirectories, assetsBase, manifest, problems, foreignRecord, parsed })` | `Array<Record<string, any>>` | reading a whole content tree into index records, in a total order independent of directory-read order; an item note yields two records (its own and its documentation journal's), and each asset root yields one per file; each note carries its resolved culture as `data.culture` |
|
|
901
|
+
| `resolveCultures` | `resolveCultures(records, { contentPackage, contentBase, foreignRecord, problems })` | `Array<Record<string, any>>` — the same records | writing each note's resolved culture into its record as `data.culture`: its own value, else its parents', else for a polity its seat's and governed places', pooled; parents that disagree give a finding into `problems`, or one thrown Error listing every conflict |
|
|
902
|
+
|
|
903
|
+
`prepareContentIndex` resolves `contentBase` from the configured `paths.content`
|
|
904
|
+
when omitted. Its `PreparedContentIndex` contains:
|
|
905
|
+
|
|
906
|
+
| Field | Value |
|
|
907
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
908
|
+
| `config` | The resolved configuration object used for preparation. |
|
|
909
|
+
| `contentBase` | The absolute source root. |
|
|
910
|
+
| `records` | The complete note, documentation and asset records, including hidden providers needed for lineage. |
|
|
911
|
+
| `parsed` | A `Map` keyed by absolute note file, holding authored `frontmatter`, `body`, `raw`, `bodyLine` and `bodyColumn`. |
|
|
912
|
+
| `expandedBodies` | A `Map` keyed by absolute note file, holding `markdown`, its body-relative `lineMap` of `{line, generated}` entries, and optional `generatedFrom`. |
|
|
913
|
+
| `foundryBodies` | A `Map` with the same body shape, prepared against the complete native compendium corpus. |
|
|
914
|
+
|
|
915
|
+
Preparation expands SQL tables, page lists, automatic sections and scalar
|
|
916
|
+
expressions. Prose declarations in `expandedBodies` use each source's audience:
|
|
917
|
+
public notes use public providers and usage; GM notes use the complete corpus.
|
|
918
|
+
Automatic lexical sections keep that source audience on every surface.
|
|
919
|
+
Explicit native SQL uses the complete corpus in `foundryBodies`.
|
|
920
|
+
|
|
921
|
+
Native own and documentation target maps derive their actual page identities
|
|
922
|
+
from `foundryBodies`. Each map contains only prose slugs declared in the
|
|
923
|
+
source-audience body and present in the native body, plus the journal's
|
|
924
|
+
reserved `$lead` target. A prose target present only on the website remains
|
|
925
|
+
valid for website links and prose-link checks; a native link to it is refused.
|
|
926
|
+
Native-only slugs and private heading names stay out of a public source's
|
|
927
|
+
metadata. Event anchors and authored source positions remain attached to their
|
|
928
|
+
sources. A published empty prose-anchor list declares no prose targets,
|
|
929
|
+
including on web-only pages.
|
|
930
|
+
|
|
931
|
+
Pass the envelope directly to the synchronous emitter:
|
|
932
|
+
|
|
933
|
+
```js
|
|
934
|
+
const publication = await prepareContentIndex({ config, contentBase });
|
|
935
|
+
const result = emitContentIndex({ publication, outDir });
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
The emitter defaults `config` and `contentBase` from the envelope. If supplied,
|
|
939
|
+
they must name that same configuration object and source root. It consumes
|
|
940
|
+
`records`, `parsed`, `expandedBodies` and `foundryBodies` without mutating them;
|
|
941
|
+
frozen inputs are valid. Every note requires a parsed source and both body
|
|
942
|
+
entries.
|
|
943
|
+
Missing or mismatched preparation is refused before creating or replacing any
|
|
944
|
+
metadata, dictionary or terms file.
|
|
945
|
+
|
|
946
|
+
Without `publication`, the synchronous emitter accepts authored bodies and
|
|
947
|
+
pure frontmatter dictionary or glossary sections. Active SQL, page lists,
|
|
948
|
+
scalar expressions, events or other generated sections require
|
|
949
|
+
`prepareContentIndex`. The CLI performs this preparation automatically.
|
|
950
|
+
`indexRecordsFor` and `collectContentIndex` collect authored records and pure
|
|
951
|
+
lexical sections; use preparation and emission to publish dynamic body targets.
|
|
876
952
|
|
|
877
953
|
`engine.contentIndex` also re-exports eight names from neighbouring leaf modules, at this same import path:
|
|
878
954
|
|
|
@@ -883,7 +959,7 @@ Emitting this package's content index. Every content build already walks the who
|
|
|
883
959
|
| `noteFile` | `noteFile(contentBase, record)` | `string` — the note's absolute path | composing an index record's `file.path` (recorded relative, for a byte-stable artifact) back into an openable absolute path |
|
|
884
960
|
| `authoredFrontmatter` | `authoredFrontmatter(record)` | `Record<string, any>` — the frontmatter without `DERIVED_KEYS` | reading a note's frontmatter back from its index record, its resolved `data.culture` included, so a pass can lint or compile from the index without reasoning about derived keys as if the author wrote them |
|
|
885
961
|
| `isNoteRecord` | `isNoteRecord(record)` | `boolean` | telling a note's own record apart from its documentation journal's and from an asset's, when enumerating the corpus |
|
|
886
|
-
| `isStub` | `isStub(record)` | `boolean` | telling a stub
|
|
962
|
+
| `isStub` | `isStub(record)` | `boolean` | telling a stub apart from a note that publishes a page; asked of the absent address, which is the whole signal |
|
|
887
963
|
| `isAssetRecord` | `isAssetRecord(record)` | `boolean` | telling a record that addresses a file apart from one that addresses a note; the `asset` block is what marks it |
|
|
888
964
|
| `recordPath` | `recordPath(record)` | `string` — the path the record names inside its package | ordering or reporting across both record shapes: a note names the `.md` it was parsed from, an asset the file it is |
|
|
889
965
|
|
|
@@ -988,7 +1064,7 @@ content tree can also compile into Foundry compendium packs.
|
|
|
988
1064
|
| `collectContentPages` | `function collectContentPages(contentBase, ctx)` | `{{pages: object[], addressFindings: object[], fmLinkFindings: object[]}}` | The content tree's pages, and what could not be addressed. |
|
|
989
1065
|
| `collectHomepages` | `function collectHomepages(contentBase, ctx)` | `{{pages: object[], addressFindings: object[]}}` The homepage notes, in walk order, and the ones among them that could not be addressed. | The package's homepage notes — the authored page at `/<contentPackage>/`. |
|
|
990
1066
|
| `writeHomepages` | `function writeHomepages(outRoot, pages, config)` | {number} How many pages were written. | Writes each homepage as the package root's `_index.md`. |
|
|
991
|
-
| `siteGates` | `
|
|
1067
|
+
| `siteGates` | `siteGates(pages, findings, options)` | {object} The gate results and, when they pass, the built index. | The integrity gates a site build runs before it writes anything. |
|
|
992
1068
|
| `emptyGates` | `function emptyGates()` | {object} An all-clear gate result. | The gate result of a build that ran none of them. |
|
|
993
1069
|
| `gatesFailed` | `function gatesFailed(gates)` | — | Whether any gate produced a finding. |
|
|
994
1070
|
| `tableUniverse` | `function tableUniverse(pages)` | {Map<string, object[]>} Package → the notes it may tabulate. | The universe a generated table searches, grouped by package. |
|
|
@@ -1000,6 +1076,10 @@ content tree can also compile into Foundry compendium packs.
|
|
|
1000
1076
|
| `sitePageDecorator` | `sitePageDecorator(config, index)` | Function | Add the site's infobox and being metadata to a rendered page. |
|
|
1001
1077
|
| `buildSite` | `buildSite({ config, sqlTables })` | `{{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], mapFindings: object[], manifests: object\|null}}` | Builds a Hugo content tree from a content tree, and reports what it found. |
|
|
1002
1078
|
|
|
1079
|
+
`siteGates` takes `{ config, records, sqlTables? }`. Pass the prepared
|
|
1080
|
+
`sqlTables` map so anchors emitted by automatic dictionary sections are present in the
|
|
1081
|
+
index for checking page links.
|
|
1082
|
+
|
|
1003
1083
|
### `engine.sitePreview`
|
|
1004
1084
|
|
|
1005
1085
|
`prepareSitePreview({ config })` reads the saved content tree and declared dependency indexes, prepares its SQL corpus, and returns a private preview context. Pass the active note's absolute path and complete buffer text to `render(file, text)`. A successful result has `{ ok: true, markdown, frontmatter, findings: [] }`; a failed result has `{ ok: false, findings }` and no replacement content. Findings identify the source file and carry a line and column when the offending text can be located.
|
|
@@ -1197,7 +1277,7 @@ Shared helpers for the pack compilers in `packages/package-build/`. The HeroicLa
|
|
|
1197
1277
|
| ------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1198
1278
|
| `md` | `const md` | — | The markdown renderer every surface shares. |
|
|
1199
1279
|
| `renderFoundryMarkdown` | `function renderFoundryMarkdown(body)` | `string` — HTML with secret sections for GM passages. | Rendering a note body for a Foundry document. |
|
|
1200
|
-
| `parseMarkdownFile` | `function parseMarkdownFile(filePath)` | — | Parses a markdown file with YAML frontmatter.
|
|
1280
|
+
| `parseMarkdownFile` | `function parseMarkdownFile(filePath)` | — | Parses a markdown file with YAML frontmatter; returns frontmatter, body, rendered description, raw source, and body coordinates. |
|
|
1201
1281
|
| `assertStatedScope` | `function assertStatedScope(skipDirectories, who)` | {void} | Refuse a corpus read whose scope its caller did not state. |
|
|
1202
1282
|
| `assertSuppliedCorpus` | `function assertSuppliedCorpus(records, who)` | {void} | Refuse a corpus read whose records its caller did not supply. |
|
|
1203
1283
|
| `statedTemplatePriority` | `function statedTemplatePriority(fm, label,` | {number\|null} The priority, or `null` when the note is not a template or states nothing. | The template priority a note states, for a system that treats an unstated one as "not a template" rather than as an authoring error. |
|
|
@@ -1210,11 +1290,16 @@ Shared helpers for the pack compilers in `packages/package-build/`. The HeroicLa
|
|
|
1210
1290
|
| `buildStats` | `function buildStats(systemVersion` | {object} The `_stats` block. | Default `_stats` block for compiled compendium entries. |
|
|
1211
1291
|
| `statsForPack` | `function statsForPack(packSystem, config` | {object} The `_stats` block for that pack. | The `_stats` block for one pack, stamped with the system that pack is for. |
|
|
1212
1292
|
| `defaultStats` | `function defaultStats()` | {object} The default `_stats` block, shared by every compiler. | The `_stats` block every compiler stamps on an entry it emits, built once. |
|
|
1213
|
-
| `buildContentLinkIndex` | `buildContentLinkIndex(contentBase, router
|
|
1293
|
+
| `buildContentLinkIndex` | `buildContentLinkIndex(contentBase, router?, options?)` | `{{byShortcode: Map, types: Set}}` From `buildWikilinkIndex`. | Indexes **every** note in the content tree so any pack compiler can resolve a wikilink to any other document. |
|
|
1214
1294
|
| `convertNoteWikilinks` | `convertNoteWikilinks(body, { type, id, pack, docPack, index, name, file, bodyLine, bodyColumn, lineMap })` | `{{markdown: string, unresolved: Array<object>}}` | Converts the wikilinks in one note's markdown, reporting any that have no target in the content tree. |
|
|
1215
1295
|
| `expandNoteTables` | `function expandNoteTables(body,` | `{{markdown: string, lineMap: Array<{line: number, generated: boolean}>}}` The body with every table expanded, and where each emitted line came from — which is what lets a diagnostic about the expanded body name an authored position. | Expand prepared SQL tables in one note's markdown, before wikilinks are resolved — so a generated cell may itself be a wikilink. |
|
|
1216
1296
|
| `folderFilename` | `function folderFilename(name, id)` | — | Builds a compendium-source filename for a folder JSON document: `folder_Name_id.json` with non-alphanumeric runs replaced by underscores. |
|
|
1217
|
-
| `walkMarkdownTree` | `walkMarkdownTree(rootDir, { skipDirectories })` | generator yielding `{frontmatter, body, description, bodyLine?, bodyColumn?, file, absPath}`
|
|
1297
|
+
| `walkMarkdownTree` | `walkMarkdownTree(rootDir, { skipDirectories })` | generator yielding `{frontmatter, body, description, raw, bodyLine?, bodyColumn?, file, absPath}` | walking a content tree and reading every markdown file's frontmatter and body; `skipDirectories` is required rather than defaulted, so two callers cannot silently disagree about the corpus's scope |
|
|
1298
|
+
|
|
1299
|
+
`buildContentLinkIndex` accepts `{ config?, records, sqlTables? }`.
|
|
1300
|
+
`records` is the required, supplied content corpus. The optional prepared
|
|
1301
|
+
`sqlTables` map supplies generated anchors and their Foundry page mappings,
|
|
1302
|
+
so glossary word links resolve to their dictionary entries.
|
|
1218
1303
|
|
|
1219
1304
|
### `engine.itemRegistry`
|
|
1220
1305
|
|
|
@@ -1807,6 +1892,12 @@ console.log(dataEnvVar("dev"));
|
|
|
1807
1892
|
| `removeContainer` | `removeContainer(name, log = () => {})` | — | stopping and removing a container, tolerating "not running" and "no such container" |
|
|
1808
1893
|
| `containerAction` | `containerAction({ action, stage, config, env, log })` | `number` | performing one `CONTAINER_ACTIONS` action for a stage end to end |
|
|
1809
1894
|
|
|
1895
|
+
`ensureS3` requires readable regular configuration files. Reuse requires an
|
|
1896
|
+
inspectable identities bind pointing to the current file; an incompatible bind
|
|
1897
|
+
throws with a scoped recovery command and leaves the server and bucket data
|
|
1898
|
+
intact. Both new and reused servers must answer their S3 HTTP health endpoint
|
|
1899
|
+
before the bucket is ensured.
|
|
1900
|
+
|
|
1810
1901
|
## `./coverage`
|
|
1811
1902
|
|
|
1812
1903
|
Whether the localization keys a package **references** and the keys it **declares** are the same set. A referenced-but-undeclared key is an error — it renders as its raw key string to a player; a declared-but-unreferenced key is advisory only, since no scan can see every way a key might be reached. Generic Foundry reference shapes (`{{localize}}`, `game.i18n.localize`, `LOCALIZATION_PREFIXES`, template literals) are read here directly; a repository that mints keys by its own convention contributes a named module through configuration. Everything exported is pure — source text in, references or findings out.
|
|
@@ -1854,7 +1945,7 @@ console.log(parseRemote("user@example.org:/srv/foundry"));
|
|
|
1854
1945
|
|
|
1855
1946
|
## `./e2e`
|
|
1856
1947
|
|
|
1857
|
-
The end-to-end harness: a disposable Foundry world, served from a container, with a browser suite driven against it. The
|
|
1948
|
+
The end-to-end harness: a disposable Foundry world, served from a container, with a browser suite driven against it. The suite is named in `packageBuild.e2e.suite`. For recognizable Cypress commands, the harness supplies pinned Chrome for Testing unless the arguments select a browser. It stands the licensed Foundry up, seeds a world with a known Gamemaster password, waits for that world to be _active_, and tears it down again. Three shapes of run answer different questions: `run` (from scratch, the only path that may change Foundry build), `fast` (the iteration loop — rebuild, redeploy, cycle, re-run), and `sweep` (the same full run against a build the repository does not pin, so `compatibility.verified` can be evidence rather than hope).
|
|
1858
1949
|
|
|
1859
1950
|
```js
|
|
1860
1951
|
import { hashPassword, E2E_MODES } from "@heroiclands/package-build/e2e";
|
|
@@ -1865,33 +1956,36 @@ console.log(E2E_MODES);
|
|
|
1865
1956
|
// -> ["run", "open"]
|
|
1866
1957
|
```
|
|
1867
1958
|
|
|
1868
|
-
| Export | Signature
|
|
1869
|
-
| ----------------------------- |
|
|
1870
|
-
| `E2E_GM_ID` | `const E2E_GM_ID`
|
|
1871
|
-
| `E2E_SCENE_ID` | `const E2E_SCENE_ID`
|
|
1872
|
-
| `E2E_MODES` | `const E2E_MODES`
|
|
1873
|
-
| `hashPassword` | `hashPassword(password, salt)`
|
|
1874
|
-
| `resolveE2EWorld` | `resolveE2EWorld(config, env = process.env)`
|
|
1875
|
-
| `worldManifest` | `worldManifest({ worldId, worldTitle, worldDescription, systemId, systemVersion, coreVersion })`
|
|
1876
|
-
| `gmDocument` | `gmDocument({ id, name, password, salt })`
|
|
1877
|
-
| `defaultSceneDocument` | `defaultSceneDocument()`
|
|
1878
|
-
| `moduleConfigurationDocument` | `moduleConfigurationDocument(packageId)`
|
|
1879
|
-
| `isWorldActive` | `isWorldActive(body)`
|
|
1880
|
-
| `resolveSweepVersion` | `resolveSweepVersion(argv)`
|
|
1881
|
-
| `parseFastArgs` | `parseFastArgs(argv, build)`
|
|
1882
|
-
| `seedTestWorld` | `async seedTestWorld({ config, packageJson, env = process.env, log = () => {}, runScript })`
|
|
1883
|
-
| `deployForContainer` | `async deployForContainer({ config, env = process.env, log = () => {}, runScript })`
|
|
1884
|
-
| `waitForWorld` | `async waitForWorld({ url, container, stage, timeoutMs = 180_000, log = () => {} })`
|
|
1885
|
-
| `
|
|
1886
|
-
| `
|
|
1887
|
-
| `
|
|
1888
|
-
| `
|
|
1889
|
-
| `
|
|
1890
|
-
| `
|
|
1891
|
-
| `
|
|
1892
|
-
| `
|
|
1893
|
-
| `
|
|
1894
|
-
| `
|
|
1959
|
+
| Export | Signature | Returns | Use it when |
|
|
1960
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1961
|
+
| `E2E_GM_ID` | `const E2E_GM_ID` | — | reading the fixed Gamemaster document id every seeded world carries |
|
|
1962
|
+
| `E2E_SCENE_ID` | `const E2E_SCENE_ID` | — | reading the seeded, pre-activated default scene's id |
|
|
1963
|
+
| `E2E_MODES` | `const E2E_MODES` | — | the closed set of modes the suite may run under (`"run"` / `"open"`) |
|
|
1964
|
+
| `hashPassword` | `hashPassword(password, salt)` | `string` — the hex hash | hashing a password the way Foundry's own `core/auth.mjs` does, to seed a login that actually works |
|
|
1965
|
+
| `resolveE2EWorld` | `resolveE2EWorld(config, env = process.env)` | `E2EWorld` | resolving the seeded world's identity from configuration, with an environment override under the repository's own variable prefix |
|
|
1966
|
+
| `worldManifest` | `worldManifest({ worldId, worldTitle, worldDescription, systemId, systemVersion, coreVersion })` | `object` — the world manifest | building the `world.json` a seeded world carries |
|
|
1967
|
+
| `gmDocument` | `gmDocument({ id, name, password, salt })` | `object` — the user document | building the seeded world's single Gamemaster user document |
|
|
1968
|
+
| `defaultSceneDocument` | `defaultSceneDocument()` | `object` — the scene document | building the one pre-activated scene every seeded world carries |
|
|
1969
|
+
| `moduleConfigurationDocument` | `moduleConfigurationDocument(packageId)` | `object` — the setting document | building the world setting that switches a module package on (a module must be activated; a system does not) |
|
|
1970
|
+
| `isWorldActive` | `isWorldActive(body)` | `boolean` | checking whether a `/join` response body shows a world that is actually active, not merely reachable |
|
|
1971
|
+
| `resolveSweepVersion` | `resolveSweepVersion(argv)` | `string` — the exact build, trimmed | reading the exact Foundry build a sweep was asked to run against; there is deliberately no default |
|
|
1972
|
+
| `parseFastArgs` | `parseFastArgs(argv, build)` | `FastArgs` | parsing the fast loop's CLI arguments against the repository's declared build table, in declaration order |
|
|
1973
|
+
| `seedTestWorld` | `async seedTestWorld({ config, packageJson, env = process.env, log = () => {}, runScript })` | `Promise<{worldDir: string, world: E2EWorld}>` | seeding the disposable world into the end-to-end stage's data root, replacing the world directory through a throwaway container so the Foundry container's user owns it |
|
|
1974
|
+
| `deployForContainer` | `async deployForContainer({ config, env = process.env, log = () => {}, runScript })` | `Promise<void>` | deploying the staged package to the end-to-end stage, removing the old install and handing the new one to the Foundry container's user through a throwaway container |
|
|
1975
|
+
| `waitForWorld` | `async waitForWorld({ url, container, stage, timeoutMs = 180_000, log = () => {} })` | `Promise<void>` | polling until a seeded world is active, or failing with a diagnosis (a licence failure is detected from the container log, not a timeout) |
|
|
1976
|
+
| `CHROME_BUILD_ID` | `const CHROME_BUILD_ID` | `string` | reading the exact Chrome for Testing build used by default for Cypress suites |
|
|
1977
|
+
| `browserCacheDir` | `browserCacheDir(env = process.env)` | `string` | locating the persistent browser cache: `HL_BROWSER_CACHE`, a sibling of `FOUNDRYVTT_CACHE`, under `HL_E2E_ROOT`, or the user's cache directory |
|
|
1978
|
+
| `prepareSuiteBrowser` | `async prepareSuiteBrowser({ command, args = [], env = process.env, platform = process.platform, arch = process.arch, log = () => {} })` | `Promise<string[]>` | appending the pinned Chrome executable to Cypress arguments, installing it when absent; explicit browser arguments and other suites pass through unchanged |
|
|
1979
|
+
| `suiteExecutables` | `suiteExecutables(command)` | `string[]` | reading every executable a suite command needs to exist before it can run at all |
|
|
1980
|
+
| `findExecutable` | `findExecutable(name, { cwd, env = process.env } = {})` | `string\|null` | finding an executable the way the child process would (`node_modules/.bin` first, then `PATH`) |
|
|
1981
|
+
| `missingExecutables` | `missingExecutables({ command, cwd, env = process.env })` | `string[]` | checking which of a suite command's executables are not there — asked again mid-run, since an install can pull the runner out from under a run in progress |
|
|
1982
|
+
| `freshResults` | `freshResults({ paths, since, cwd })` | `string[]` | finding which declared result paths a suite run actually wrote to since it was spawned (existence alone is not evidence) |
|
|
1983
|
+
| `suiteVerdict` | `suiteVerdict({ status, vanished = [], declared = [], fresh = [] })` | `SuiteVerdict` | deciding what a finished suite run is worth given its exit status and what it left behind; can only ever downgrade a verdict, never upgrade one |
|
|
1984
|
+
| `runSuite` | `runSuite({ command, args = [], cwd, results = [], env = process.env, log = () => {} })` | `number` — the suite's exit status | running the repository's suite, bracketed by before/after executable checks and a fresh-results check rather than trusting the exit status alone |
|
|
1985
|
+
| `suiteCommand` | `suiteCommand(config, mode)` | `string[]` | reading the declared suite command for a mode (`"run"` or `"open"`), or failing clearly when none is declared |
|
|
1986
|
+
| `e2eRun` | `async e2eRun({ config, packageJson, mode = "run", suiteArgs = [], env = process.env, log = () => {} })` | `Promise<number>` | running a full, from-scratch end-to-end pass: deploy, reseed, recreate the container, wait, run the suite, tear down (or leave it serving in `"open"` mode) |
|
|
1987
|
+
| `e2eFast` | `async e2eFast({ config, argv = [], env = process.env, log = () => {} })` | `Promise<number>` | iterating quickly during development: rebuild what changed, redeploy, cycle the world, wait, re-run |
|
|
1988
|
+
| `e2eSweep` | `async e2eSweep({ config, packageJson, argv = [], env = process.env, log = () => {} })` | `Promise<number>` | running the full suite against a build the repository does not pin, to license moving `compatibility.verified` forward |
|
|
1895
1989
|
|
|
1896
1990
|
## `./lang`
|
|
1897
1991
|
|
|
@@ -2027,6 +2121,256 @@ The engine wildcard subpath also exposes the body parsers below. Their figure ex
|
|
|
2027
2121
|
|
|
2028
2122
|
`scanFigures` retains `line` (1-based caption line), `bodyStart`/`bodyEnd` (0-based, exclusive-end target range), and `close` (last consumed target line), plus caption attributes, kind, label and number. Unnumbered captions have an empty label and consume no number. `collectAnchors` discovers heading, caption, div, poem and span identifiers; `markupAnchorFindings(body)` reports collisions involving non-page identifiers; Foundry maps non-page anchors to the actual containing page UUID. An unnumbered `ref` requires `form="title"` or `form="full"`.
|
|
2029
2123
|
|
|
2124
|
+
`engine/markup-validation.mjs` provides shared syntax validation:
|
|
2125
|
+
|
|
2126
|
+
| Export | Purpose |
|
|
2127
|
+
| ---------------- | ----------------------------------------------------------------------------------- |
|
|
2128
|
+
| `markupFindings` | Validate complete Markdown source and return diagnostics for the supplied filename. |
|
|
2129
|
+
|
|
2130
|
+
`markupFindings(source, file)` masks YAML frontmatter without moving source positions and returns block and alert scanner errors as `{file, line, column, severity, message}` diagnostics. Content lint, Markdown checks and formatting preflight share this validation; malformed files are not rewritten by formatting or Markdown fixes.
|
|
2131
|
+
|
|
2030
2132
|
`engine/content-alerts.mjs` exposes `ALERT_TYPES`, `scanAlerts(source)` and `renderAlerts(source, target)` and `alertMarkdownPlugin(md)`. The scanner returns `{blocks, errors}`; alert records carry `type`, `id`, `classes`, `attributes`, `title`, `body` and inclusive zero-based `start`/`end` source lines. The renderer produces attributed `aside.alert` wrappers while leaving alert body Markdown in the document's scope. Alert validation and anchor discovery run in site, Foundry and book pipelines.
|
|
2031
2133
|
|
|
2032
2134
|
Alert `title` is the displayed heading override; absent, it defaults to the label in `ALERT_TYPES`. It does not change the alert type’s icon or color.
|
|
2135
|
+
|
|
2136
|
+
The `parsed` map accepted by content-index collection is filled by absolute
|
|
2137
|
+
note path with `{ frontmatter, body, raw, bodyLine?, bodyColumn? }`. Frontmatter
|
|
2138
|
+
is the authored value before index derivation. `buildCompileCorpus` returns
|
|
2139
|
+
that map as `parsed` for its document passes. `unclaimedNoteFindings` accepts it
|
|
2140
|
+
alongside `records`. `locateFrontmatterKey` and `assertNoDeclaredPackage` accept
|
|
2141
|
+
`raw` in their options to locate findings without opening the note again.
|
|
2142
|
+
|
|
2143
|
+
`prepareSqlTables` accepts sources with an optional `authoredSqlBlocks` count,
|
|
2144
|
+
set by `joinGeneratedSections`. Subsequent fences are generated event queries
|
|
2145
|
+
whose stub-inclusive results are identical; only authored fences receive a
|
|
2146
|
+
stub-inclusion comparison. Query answers and comparisons are cached for one
|
|
2147
|
+
preparation and kept separate.
|
|
2148
|
+
|
|
2149
|
+
`prepareTreeSqlTables` retains the complete record membership when opening a
|
|
2150
|
+
public database, so derived citations can distinguish hidden notes from
|
|
2151
|
+
unknown addresses. Only public records contribute rendered sources and links.
|
|
2152
|
+
|
|
2153
|
+
| Export | Contract |
|
|
2154
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
2155
|
+
| `prepareLinkSqlTables` | Prepare SQL, page-list and automatic lexical sections for link checking, using one preparation per required source audience. |
|
|
2156
|
+
|
|
2157
|
+
`engine/sql-tables.mjs` exports `prepareLinkSqlTables(contentBase, options)`.
|
|
2158
|
+
Its options are `config`, `skipDirectories`, complete `records`, the shared
|
|
2159
|
+
`parsed` map and `foreignIndex`. It returns prepared tables with `pageLists`
|
|
2160
|
+
and `inline` maps, or `undefined` when no source needs a directive or automatic
|
|
2161
|
+
lexical section. Public
|
|
2162
|
+
sources use public rows; GM sources use all rows. Query results remain local
|
|
2163
|
+
to each audience preparation, and the caller’s records remain complete for
|
|
2164
|
+
checking authored references. Unresolved derived citations retain the
|
|
2165
|
+
renderer’s plain-label behavior.
|
|
2166
|
+
|
|
2167
|
+
`generatedSectionsFor(db, records, contentBase, options?)` accepts complete
|
|
2168
|
+
lineage records as `options.all` (default `records`). Its result includes
|
|
2169
|
+
`lexical`, a map from note file to `LexicalSection[]`, beside `sections`,
|
|
2170
|
+
`views` and `derived`. `attachGeneratedSections(prepared, generated)` places
|
|
2171
|
+
those lists on each note's prepared results as `lexical`; body expansion
|
|
2172
|
+
inserts them alongside the prepared directives.
|
|
2173
|
+
|
|
2174
|
+
## Dictionary and glossary rows
|
|
2175
|
+
|
|
2176
|
+
The wildcard engine subpath exposes
|
|
2177
|
+
`@heroiclands/package-build/engine/dictionary-rows` for deriving the
|
|
2178
|
+
`dictionary` and `terms` SQL relations from content-index records:
|
|
2179
|
+
|
|
2180
|
+
| Export | Signature | Returns | Use it when |
|
|
2181
|
+
| ----------------------- | ----------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------- |
|
|
2182
|
+
| `DICTIONARY_COLUMNS` | `const DICTIONARY_COLUMNS` | A readonly list of column names | Declaring the schema of a language's dictionary. |
|
|
2183
|
+
| `TERM_COLUMNS` | `const TERM_COLUMNS` | A readonly list of column names | Declaring the schema of owner-specific glossary terms. |
|
|
2184
|
+
| `ETYMOLOGY_KEYS` | `const ETYMOLOGY_KEYS` | A readonly list of keys | Reading the structure of a dictionary word's etymology. |
|
|
2185
|
+
| `dictionaryLetter` | `dictionaryLetter(text)` | `string` | Deriving a heading letter from a sort key. |
|
|
2186
|
+
| `dictionaryEntryAnchor` | `dictionaryEntryAnchor(language, name)` | `string` | Deriving the stable entry anchor for a word in a language's dictionary. |
|
|
2187
|
+
| `dictionaryAndTermRows` | `dictionaryAndTermRows(records, all = records)` | `{ dictionary: object[], terms: object[] }` | Deriving dictionary words and glossary terms for the selected records. |
|
|
2188
|
+
| `serializeRows` | `serializeRows(rows)` | `string` | Writing deterministic JSON Lines with keys sorted at every depth. |
|
|
2189
|
+
|
|
2190
|
+
`dictionaryEntryAnchor` takes the language's canonical Address and the
|
|
2191
|
+
word's form. Its `word-` anchor is derived from the full SHA-256 digest of
|
|
2192
|
+
that language and the NFC-normalized form, with case and accents preserved.
|
|
2193
|
+
It identifies the word's entry on that language page, including a word
|
|
2194
|
+
inherited from an ancestor.
|
|
2195
|
+
|
|
2196
|
+
`records` is the selected audience; `all` supplies the complete record set for
|
|
2197
|
+
language and ownership resolution. A dictionary word takes its sort key and
|
|
2198
|
+
pronunciation from the language's word declaration. A glossary entry can
|
|
2199
|
+
supply its own sort key and pronunciation, using the dictionary word's
|
|
2200
|
+
values where unstated.
|
|
2201
|
+
|
|
2202
|
+
A term naming a dictionary word carries its `class` and a `word` Address
|
|
2203
|
+
linking to its dictionary entry. The target is in the resolved language's
|
|
2204
|
+
dictionary when that language has own words; otherwise it is in the visible
|
|
2205
|
+
language that defines the word. The term's `language` stays resolved to the
|
|
2206
|
+
owner or entry's language. Without an entry's `definition`, its `senses`
|
|
2207
|
+
contain every dictionary gloss sense with
|
|
2208
|
+
an empty `bodies` list. With a definition, that owner's meaning is the sense
|
|
2209
|
+
instead. A rank or office of the same owner contributes its distinct
|
|
2210
|
+
descriptions after the dictionary senses; the owner's definition replaces
|
|
2211
|
+
both. Ranks, offices and terms outside the dictionary carry their source
|
|
2212
|
+
bodies. `common` belongs to
|
|
2213
|
+
glossary entries and `terms`, with each owner's choice kept on that owner's
|
|
2214
|
+
row. The [format
|
|
2215
|
+
reference](reference/format-details.md#dictionaries-and-glossaries) defines
|
|
2216
|
+
all columns and the rules for culture reach and ranks. The `cultures` list
|
|
2217
|
+
records associations for custom queries. Automatic Glossary sections select
|
|
2218
|
+
only their owner's explicitly declared `data.glossary` entries. A custom
|
|
2219
|
+
view can also select reached rank and office rows, keeping common marks only
|
|
2220
|
+
on its owner's own rows.
|
|
2221
|
+
|
|
2222
|
+
Dictionary rows carry `plural`, the noun's nonempty explicit plural or its
|
|
2223
|
+
listed language's first matching literal suffix replacement, and `verbs`,
|
|
2224
|
+
that language's stated `{invariant: true}` or six-person pattern, otherwise
|
|
2225
|
+
null. These optional facts use no ancestor grammar and perform no word-rule
|
|
2226
|
+
validation. The raw `words` relation's `plural` remains the authored value.
|
|
2227
|
+
Dictionary SQL types are projected only for that relation, retaining its
|
|
2228
|
+
etymology struct, sense list, usage list and integer depth even when all
|
|
2229
|
+
optional grammar values are null.
|
|
2230
|
+
|
|
2231
|
+
`@heroiclands/package-build/engine/language-terms` exposes the person
|
|
2232
|
+
descriptors used by the schema, SQL pattern type and head table:
|
|
2233
|
+
|
|
2234
|
+
| Export | Signature | Returns | Use it when |
|
|
2235
|
+
| -------------------- | -------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------- |
|
|
2236
|
+
| `VERB_PERSON_FIELDS` | `const VERB_PERSON_FIELDS` | Readonly inner-key descriptors | Reading the six present person names, labels and ending-string shapes in presentation order. |
|
|
2237
|
+
|
|
2238
|
+
`serializeRows` returns the empty string for no rows; otherwise each object
|
|
2239
|
+
occupies one line and the file ends with a newline.
|
|
2240
|
+
|
|
2241
|
+
## Dictionary and glossary layouts
|
|
2242
|
+
|
|
2243
|
+
`@heroiclands/package-build/engine/dictionary-layout` renders SQL results as
|
|
2244
|
+
Markdown for the dictionary or glossary on its owner's note:
|
|
2245
|
+
|
|
2246
|
+
| Export | Signature | Returns | Use it when |
|
|
2247
|
+
| ------------------------- | ------------------------------------ | -------------------------- | ----------------------------------------------------------------------------------- |
|
|
2248
|
+
| `LAYOUT_REQUIRED_COLUMNS` | `const LAYOUT_REQUIRED_COLUMNS` | Required columns by layout | Checking the columns a dictionary or glossary query must select. |
|
|
2249
|
+
| `COMMON_MARK` | `const COMMON_MARK` | `string` | Reading the mark a common glossary entry opens with. |
|
|
2250
|
+
| `DICTIONARY_ENTRY_CLASS` | `const DICTIONARY_ENTRY_CLASS` | `string` | Identifying generated headword spans that are native dictionary targets in Foundry. |
|
|
2251
|
+
| `dictionaryEntryPlugin` | `dictionaryEntryPlugin(md)` | `void` | Registering native dictionary headword rendering on the Foundry Markdown parser. |
|
|
2252
|
+
| `renderDictionary` | `renderDictionary(result, options?)` | `string` | Rendering a language's words with classes, etymology, senses and citations. |
|
|
2253
|
+
| `renderGlossary` | `renderGlossary(result, options?)` | `string` | Rendering an owner's glossary entries, ranks and offices. |
|
|
2254
|
+
|
|
2255
|
+
`result` is `{ columns: string[], rows: object[] }`. Both layouts require
|
|
2256
|
+
`name`, `senses` and `usage`; a missing column throws an error naming it.
|
|
2257
|
+
`LayoutOptions` accepts `linkable(ref)`, `sectionLevel`, `addressContext`,
|
|
2258
|
+
`labelOf(address)`, `showKey` (`true`, `false` or `"only"`), `pageLanguage`, `pageOwner`,
|
|
2259
|
+
`verbs` and `entryAnchors` (default `true`). These control link availability, letter-heading level,
|
|
2260
|
+
Address resolution, labels, the abbreviation key and the language whose tag
|
|
2261
|
+
is omitted. `entryAnchors` controls canonical dictionary headword targets;
|
|
2262
|
+
custom SQL dictionary views set it to `false`, leaving those targets to the
|
|
2263
|
+
language's automatic Dictionary section. A dictionary marks nothing common;
|
|
2264
|
+
a glossary prints the mark and its explanation when a selected term carries
|
|
2265
|
+
`common: true`.
|
|
2266
|
+
|
|
2267
|
+
A dictionary entry has a stable word anchor. A glossary headword with a
|
|
2268
|
+
`word` Address links to that entry, and its `class` prints beside the language
|
|
2269
|
+
tag. Glossary rows sharing a headword and language combine into one entry;
|
|
2270
|
+
identical senses print once with all their source bodies. Dictionary-derived
|
|
2271
|
+
senses carry no body label. Dictionary entries print their `field` in italic
|
|
2272
|
+
parentheses and their etymology in brackets.
|
|
2273
|
+
|
|
2274
|
+
Nouns print their optional `plural` beside the class. Each represented
|
|
2275
|
+
language's optional `verbs` column supplies one table after the key and
|
|
2276
|
+
before the first letter, with empty endings shown as "unchanged". For an
|
|
2277
|
+
automatic dictionary, `options.verbs` supplies the owning language's pattern
|
|
2278
|
+
even when its own words contain only excluded classes. The existing
|
|
2279
|
+
nonempty-own-words gate still applies. `showKey: false` suppresses the key
|
|
2280
|
+
and verb table while retaining entries; `showKey: "only"` prints the
|
|
2281
|
+
abbreviation key alone. With the default or `showKey: true`, a stated pattern
|
|
2282
|
+
still prints when the result needs no abbreviation key. Each head-enabled
|
|
2283
|
+
result prints its represented languages' tables independently.
|
|
2284
|
+
|
|
2285
|
+
`pageOwner` is the containing page's canonical note Address. A glossary omits
|
|
2286
|
+
a sense's body label only when every contributing row belongs to that page
|
|
2287
|
+
and attributes the sense solely to itself. Other bodies and shared senses
|
|
2288
|
+
keep their labels. Without `pageOwner` or a selected `owner` column, all body
|
|
2289
|
+
labels remain.
|
|
2290
|
+
|
|
2291
|
+
`DICTIONARY_ENTRY_CLASS` is `"dictionary-entry"`. The Foundry parser's
|
|
2292
|
+
`dictionaryEntryPlugin` promotes the marked headword at the start of a
|
|
2293
|
+
dictionary paragraph to an inline native H6 heading in a div container. It
|
|
2294
|
+
keeps the containing journal page. Site and book rendering retain the
|
|
2295
|
+
headword's span and paragraph. `anchorsOf` maps this dictionary target to
|
|
2296
|
+
its containing page's UUID with the word fragment; generic authored spans
|
|
2297
|
+
keep their containing-page behavior.
|
|
2298
|
+
|
|
2299
|
+
## Automatic dictionary and glossary sections
|
|
2300
|
+
|
|
2301
|
+
`@heroiclands/package-build/engine/lexical-sections` derives and inserts the
|
|
2302
|
+
same automatic lists for the site, Foundry journal, book and published index:
|
|
2303
|
+
|
|
2304
|
+
| Export | Signature | Returns | Use it when |
|
|
2305
|
+
| ----------------------- | ------------------------------------------------------ | ------------------------------------- | ----------------------------------------------------------------- |
|
|
2306
|
+
| `LEXICAL_SECTIONS` | `const LEXICAL_SECTIONS` | A frozen list of section declarations | Reading section headings, accepted anchors and frontmatter gates. |
|
|
2307
|
+
| `hasLexicalEntries` | `hasLexicalEntries(frontmatter)` | `boolean` | Checking whether a note supplies automatic lexical content. |
|
|
2308
|
+
| `lexicalSectionsFor` | `lexicalSectionsFor(records, all = records)` | `Map<string, LexicalSection[]>` | Deriving lists keyed by their owner's canonical Address. |
|
|
2309
|
+
| `insertLexicalSections` | `insertLexicalSections(markdown, sections?, context?)` | `{markdown, lineMap, generatedFrom?}` | Inserting lists while preserving authored text and source lines. |
|
|
2310
|
+
|
|
2311
|
+
Each registry declaration has `slug`, `heading`, `aliases`, `dataKey` and
|
|
2312
|
+
`types`, whose entries have `type` and an optional `subType`. The Dictionary
|
|
2313
|
+
declaration applies to `skill`/`language` notes with nonempty `data.words`,
|
|
2314
|
+
and accepts `dictionary` and `lexicon` heading anchors. The Glossary
|
|
2315
|
+
declaration applies to cultures (`lore`/`culture`) and affiliations with
|
|
2316
|
+
nonempty `data.glossary`, and accepts `glossary`. `hasLexicalEntries` reads
|
|
2317
|
+
these same type and nonempty-array gates; it does not validate the authored
|
|
2318
|
+
body.
|
|
2319
|
+
|
|
2320
|
+
`records` is the visible audience; `all` supplies complete lineage records.
|
|
2321
|
+
Each `LexicalSection` has `slug`, `heading`, `aliases`, a query-shaped
|
|
2322
|
+
`result: {columns, rows}` and dictionary-layout `options`. Dictionary rows
|
|
2323
|
+
cover the language's full dictionary; glossary rows select its owner's
|
|
2324
|
+
explicit entries only, including a rank or office only when named there.
|
|
2325
|
+
A qualifying words declaration containing only excluded word classes still
|
|
2326
|
+
produces its Dictionary heading, without entries or a key.
|
|
2327
|
+
|
|
2328
|
+
`insertLexicalSections` takes the body after authored directives expand.
|
|
2329
|
+
Pass the original body as `context.authored` together with the expansion's
|
|
2330
|
+
line map, so generated headings cannot select or end an authored section.
|
|
2331
|
+
Without `authored`, the helper treats `markdown` as its source. It inserts a
|
|
2332
|
+
list at the end of the first matching authored anchored section, after its
|
|
2333
|
+
text and before the next authored heading at the same or a higher level.
|
|
2334
|
+
Headings inside comments, raw HTML, literal code or named blocks do not select
|
|
2335
|
+
sections.
|
|
2336
|
+
Without that heading, it appends an H1 with the section's canonical anchor.
|
|
2337
|
+
Letter headings sit one level below the containing heading, up to H6. The
|
|
2338
|
+
optional context is `{authored?, lineMap?, generatedFrom?}`; the returned line map keeps
|
|
2339
|
+
authored positions, marks inserted lines as generated and adjusts the start
|
|
2340
|
+
of appended nonlexical sections. It leaves the supplied line-map array
|
|
2341
|
+
unchanged.
|
|
2342
|
+
|
|
2343
|
+
## Glossary term validation
|
|
2344
|
+
|
|
2345
|
+
The wildcard module `@heroiclands/package-build/engine/culture-reach`
|
|
2346
|
+
exposes the term lookup used for glossary validation;
|
|
2347
|
+
`@heroiclands/package-build/engine/glossary-entries` exposes its findings:
|
|
2348
|
+
|
|
2349
|
+
| Export | Signature | Returns | Use it when |
|
|
2350
|
+
| --------------- | ------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
|
|
2351
|
+
| `noteTerms` | `noteTerms(index, note, entry?)` | `Map<string, string[]>` | Reading eligible dictionary words and reached rank or office descriptions by NFC name. |
|
|
2352
|
+
| `checkGlossary` | `checkGlossary(note, { index } = {})` | `object[]` | Checking the meanings and language references of a culture's or affiliation's glossary entries. |
|
|
2353
|
+
|
|
2354
|
+
`noteTerms` selects words from the entry's resolved language and its
|
|
2355
|
+
ancestors; without `entry`, it uses the note's own resolved language. It also
|
|
2356
|
+
includes ranks and offices that reach the note. `checkGlossary` returns
|
|
2357
|
+
located findings for entries naming no language, duplicate names, invalid
|
|
2358
|
+
pronunciations and entries without a definition that match neither a word in
|
|
2359
|
+
their resolved language nor a reached rank or office. A definition repeating
|
|
2360
|
+
the only sense it replaces produces a warning. With no `index`, the check
|
|
2361
|
+
validates only what the note states locally.
|
|
2362
|
+
|
|
2363
|
+
## Built-piece resolution
|
|
2364
|
+
|
|
2365
|
+
The wildcard engine subpath exposes `pieceGlosses` from
|
|
2366
|
+
`@heroiclands/package-build/engine/word-rows`.
|
|
2367
|
+
|
|
2368
|
+
| Export | Signature | Returns | Use it when |
|
|
2369
|
+
| -------------- | ------------------------ | ----------------------------------- | ---------------------------------------------------------------------------- |
|
|
2370
|
+
| `pieceGlosses` | `pieceGlosses(...datas)` | A lookup returning a string or null | Resolving a word's built pieces for lint, word rows or dictionary etymology. |
|
|
2371
|
+
|
|
2372
|
+
Pass language `data` objects nearest first, in `lineageChain` order. Each
|
|
2373
|
+
language contributes words, affixes and elements, in that order. Words and
|
|
2374
|
+
affixes supply `gloss`, including an affix matched by its `front` form. Elements
|
|
2375
|
+
supply `sense` and match their `form` and `variants`. Exact matches precede matches without joining hyphens; case and
|
|
2376
|
+
accents are preserved. A missing gloss returns `null`.
|