@heroiclands/package-build 17.1.0 → 18.0.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 +1773 -0
- package/CONTENT.md +288 -72
- package/README.md +1 -1
- package/bin/content-build.mjs +218 -137
- package/bin/package-build.mjs +43 -0
- package/content-config.mjs +62 -67
- package/docs/content-format.md +768 -58
- package/engine/actor-compiler.mjs +586 -0
- package/engine/address-charset.mjs +11 -8
- package/engine/address-diff.mjs +266 -22
- package/engine/anchored-sections.mjs +83 -0
- package/engine/anchors.mjs +83 -0
- package/engine/base-compiler.mjs +148 -18
- package/engine/bundle-notes.mjs +276 -0
- package/engine/bundles.mjs +307 -0
- package/engine/code-fences.mjs +103 -0
- package/engine/compile-corpus.mjs +89 -0
- package/engine/content-address.mjs +267 -5
- package/engine/content-format-check.mjs +54 -11
- package/engine/content-format.mjs +37 -5
- package/engine/content-index.mjs +394 -111
- package/engine/content-links.mjs +185 -77
- package/engine/content-lint.mjs +232 -14
- package/engine/content-package.mjs +2 -1
- package/engine/content-tables.mjs +99 -3
- package/engine/document-subtypes.mjs +49 -6
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +56 -6
- package/engine/folder-notes.mjs +470 -0
- package/engine/foreign-catalog.mjs +195 -4
- package/engine/{manifest-emit.mjs → foundry-entries.mjs} +78 -81
- package/engine/frontmatter-lint.mjs +245 -19
- package/engine/frontmatter.mjs +88 -8
- package/engine/generate.mjs +304 -13
- package/engine/helpers.mjs +293 -64
- package/engine/ids.mjs +137 -2
- package/engine/index-records.mjs +126 -0
- package/engine/index.mjs +23 -5
- package/engine/item-compiler.mjs +349 -0
- package/engine/item-docs.mjs +7 -1
- package/engine/item-registry.mjs +6 -0
- package/engine/journals.mjs +93 -27
- package/engine/macros.mjs +4 -2
- package/engine/metadata-index.mjs +495 -0
- package/engine/note-claims.mjs +146 -25
- package/engine/note-ids.mjs +112 -0
- package/engine/note-renames.mjs +134 -0
- package/engine/note-schemas.mjs +25 -0
- package/engine/note-vocabulary.mjs +110 -27
- package/engine/pack-config.mjs +4 -0
- package/engine/pack-router.mjs +23 -0
- package/engine/retired-fields.mjs +137 -3
- package/engine/scenes.mjs +66 -34
- package/engine/schema-check.mjs +58 -17
- package/engine/site-build.mjs +71 -12
- package/engine/site-index.mjs +20 -7
- package/engine/sql-tables.mjs +485 -0
- package/engine/subtype-registry.mjs +102 -0
- package/engine/system-block.mjs +96 -12
- package/engine/systems.mjs +201 -0
- package/engine/web-wikilinks.mjs +50 -20
- package/engine/wikilink-syntax.mjs +17 -8
- package/engine/wikilinks.mjs +124 -51
- package/engine/yaml-lint.mjs +282 -0
- package/hm3/actors.mjs +352 -0
- package/hm3/default-item-art.mjs +75 -0
- package/hm3/document-subtypes.mjs +134 -0
- package/hm3/index.mjs +56 -0
- package/hm3/item-builders.mjs +84 -0
- package/hm3/item-fields.mjs +180 -0
- package/hm3/items.mjs +96 -0
- package/hm3/template-priority.mjs +85 -0
- package/labels.mjs +191 -0
- package/manifest.mjs +43 -2
- package/package.json +19 -4
- package/release.mjs +62 -7
- package/sohl/actors.mjs +33 -487
- package/sohl/being-info.mjs +16 -7
- package/sohl/default-item-art.mjs +14 -3
- package/sohl/document-subtypes.mjs +16 -10
- package/sohl/item-builders.mjs +14 -5
- package/sohl/item-fields.mjs +68 -7
- package/sohl/items.mjs +44 -258
- package/sohl/note-schemas.mjs +2 -2
- package/types/content-config.d.mts +21 -42
- package/types/engine/actor-compiler.d.mts +204 -0
- package/types/engine/address-charset.d.mts +11 -8
- package/types/engine/address-diff.d.mts +53 -5
- package/types/engine/anchored-sections.d.mts +21 -0
- package/types/engine/anchors.d.mts +20 -0
- package/types/engine/base-compiler.d.mts +17 -17
- package/types/engine/bundle-notes.d.mts +173 -0
- package/types/engine/bundles.d.mts +60 -0
- package/types/engine/code-fences.d.mts +43 -0
- package/types/engine/compile-corpus.d.mts +32 -0
- package/types/engine/content-address.d.mts +205 -5
- package/types/engine/content-format-check.d.mts +6 -2
- package/types/engine/content-format.d.mts +57 -1
- package/types/engine/content-index.d.mts +64 -80
- package/types/engine/content-links.d.mts +52 -7
- package/types/engine/content-lint.d.mts +10 -1
- package/types/engine/content-package.d.mts +2 -1
- package/types/engine/content-tables.d.mts +21 -39
- package/types/engine/document-subtypes.d.mts +37 -3
- package/types/engine/field-spec.d.mts +76 -5
- package/types/engine/folder-notes.d.mts +159 -0
- package/types/engine/foreign-catalog.d.mts +53 -0
- package/types/engine/{manifest-emit.d.mts → foundry-entries.d.mts} +16 -38
- package/types/engine/frontmatter-lint.d.mts +10 -2
- package/types/engine/frontmatter.d.mts +64 -0
- package/types/engine/generate.d.mts +38 -0
- package/types/engine/helpers.d.mts +94 -30
- package/types/engine/ids.d.mts +96 -0
- package/types/engine/index-records.d.mts +68 -0
- package/types/engine/index.d.mts +9 -3
- package/types/engine/item-compiler.d.mts +131 -0
- package/types/engine/journals.d.mts +47 -9
- package/types/engine/metadata-index.d.mts +226 -0
- package/types/engine/note-claims.d.mts +57 -11
- package/types/engine/note-ids.d.mts +38 -0
- package/types/engine/note-renames.d.mts +102 -0
- package/types/engine/note-vocabulary.d.mts +44 -8
- package/types/engine/retired-fields.d.mts +75 -0
- package/types/engine/scenes.d.mts +3 -2
- package/types/engine/schema-check.d.mts +25 -4
- package/types/engine/site-build.d.mts +4 -4
- package/types/engine/site-index.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +185 -0
- package/types/engine/subtype-registry.d.mts +49 -0
- package/types/engine/system-block.d.mts +40 -1
- package/types/engine/systems.d.mts +106 -0
- package/types/engine/web-wikilinks.d.mts +4 -2
- package/types/engine/wikilink-syntax.d.mts +10 -3
- package/types/engine/wikilinks.d.mts +41 -13
- package/types/engine/yaml-lint.d.mts +107 -0
- package/types/hm3/actors.d.mts +48 -0
- package/types/hm3/default-item-art.d.mts +42 -0
- package/types/hm3/document-subtypes.d.mts +24 -0
- package/types/hm3/index.d.mts +7 -0
- package/types/hm3/item-builders.d.mts +11 -0
- package/types/hm3/item-fields.d.mts +12 -0
- package/types/hm3/items.d.mts +23 -0
- package/types/hm3/template-priority.d.mts +21 -0
- package/types/manifest.d.mts +22 -1
- package/types/release.d.mts +9 -4
- package/types/sohl/actors.d.mts +7 -74
- package/types/sohl/being-info.d.mts +8 -27
- package/types/sohl/default-item-art.d.mts +5 -3
- package/types/sohl/items.d.mts +17 -32
- package/engine/foreign-manifests.mjs +0 -126
- package/engine/kb-manifest.mjs +0 -490
- package/types/engine/foreign-manifests.d.mts +0 -43
- package/types/engine/kb-manifest.d.mts +0 -241
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export class Hm3Actors extends SystemActorCompiler {
|
|
2
|
+
/**
|
|
3
|
+
* HM3's note-type → document-subtype map — the one declaration that says
|
|
4
|
+
* which block this pass reads, which notes it claims, and what each
|
|
5
|
+
* becomes. It is also what every embedded reference is translated through.
|
|
6
|
+
*
|
|
7
|
+
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
8
|
+
*/
|
|
9
|
+
static documentSubtypes: import("../engine/document-subtypes.mjs").DocumentSubtypeMap;
|
|
10
|
+
/**
|
|
11
|
+
* Build every embedded item an HM3 actor carries, from `hm3.items`.
|
|
12
|
+
*
|
|
13
|
+
* One list, not two: HM3 keeps a character's abilities in `system.abilities`
|
|
14
|
+
* rather than as embedded documents, so there is no attributes map to
|
|
15
|
+
* expand the way SoHL's pass expands `sohl.attributes`.
|
|
16
|
+
*
|
|
17
|
+
* @param {Map<string, object>} itemsMap - The predefined items, by address.
|
|
18
|
+
* @param {string} actorId - The owning actor's id, seeding embedded ids.
|
|
19
|
+
* @param {object} fm - The note's frontmatter.
|
|
20
|
+
* @param {string} ctx - Diagnostic context (the actor's label).
|
|
21
|
+
* @returns {object[]} The embedded items.
|
|
22
|
+
*/
|
|
23
|
+
buildEmbeddedItems(itemsMap: Map<string, object>, actorId: string, fm: object, ctx: string): object[];
|
|
24
|
+
/**
|
|
25
|
+
* Build one HM3 actor document from a `being` note.
|
|
26
|
+
*
|
|
27
|
+
* @param {Map<string, object>} itemsMap - The predefined items, by address.
|
|
28
|
+
* @param {object} fm - The note's frontmatter.
|
|
29
|
+
* @param {string} body - The note body, converted.
|
|
30
|
+
* @returns {object} The actor document, keyed for the pack.
|
|
31
|
+
*/
|
|
32
|
+
buildActor(itemsMap: Map<string, object>, fm: object, body: string): object;
|
|
33
|
+
/**
|
|
34
|
+
* The document's `flags`: whatever the note authors, plus this system's
|
|
35
|
+
* template priority.
|
|
36
|
+
*
|
|
37
|
+
* The rule itself is {@link module:hm3/template-priority.templateFlags},
|
|
38
|
+
* because the Item pass writes the same flag from the same statement (#283)
|
|
39
|
+
* and two copies of it were one copy too many — this pass had the only one,
|
|
40
|
+
* and the Item pass had none.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} fm - The note's frontmatter.
|
|
43
|
+
* @param {string} block - This pass's system block.
|
|
44
|
+
* @returns {object} The flags to emit.
|
|
45
|
+
*/
|
|
46
|
+
actorFlags(fm: object, block: string): object;
|
|
47
|
+
}
|
|
48
|
+
import { SystemActorCompiler } from "../engine/actor-compiler.mjs";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default art path for an HM3 item type, or throw when the type is unknown.
|
|
3
|
+
*
|
|
4
|
+
* Reading this from `item-builders.mjs` is what keeps the two lists one: that
|
|
5
|
+
* module cannot declare a type this map does not cover, because this throws and
|
|
6
|
+
* that module evaluates at import.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} type - The note type.
|
|
9
|
+
* @returns {string} The default image path for it.
|
|
10
|
+
* @throws {Error} When no art is paired with the type.
|
|
11
|
+
*/
|
|
12
|
+
export function hm3DefaultItemArt(type: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Default HM3 item artwork, keyed by the **note** type that compiles into it.
|
|
15
|
+
*
|
|
16
|
+
* The registry contract is fail-fast (see {@link module:engine/item-registry.itemArt}):
|
|
17
|
+
* a type whose entry pairs no art aborts the pack build for a note that carries
|
|
18
|
+
* no `img:` of its own, rather than shipping a mismatched icon. So every type
|
|
19
|
+
* `item-builders.mjs` declares has a row here.
|
|
20
|
+
*
|
|
21
|
+
* **Paths are fully resolved.** `resolveImg` rewrites a leading `icons/` or
|
|
22
|
+
* `images/` to the *consuming package's* asset root, which is not where these
|
|
23
|
+
* live: they are shipped by the HM3 system. Written as `systems/hm3/images/…`
|
|
24
|
+
* they pass through untouched and address the icons HM3's own compendiums
|
|
25
|
+
* already use, so an item compiled from a note looks like its hand-authored
|
|
26
|
+
* neighbours.
|
|
27
|
+
*
|
|
28
|
+
* **A one-to-many type gets one default**, because art is keyed by note type
|
|
29
|
+
* and a note type is what a registry entry addresses. `weapongear` compiles
|
|
30
|
+
* into a weapon or a missile and defaults to the sword either way; a note whose
|
|
31
|
+
* subject is a thrown spear says so with an `img:` of its own. Keying art by
|
|
32
|
+
* document subtype instead would mean a second lookup that could disagree with
|
|
33
|
+
* the registry's, and the note-level override already answers the case.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Note type → the image an HM3 item of that type ships with.
|
|
39
|
+
*
|
|
40
|
+
* @type {Readonly<Record<string, string>>}
|
|
41
|
+
*/
|
|
42
|
+
export const HM3_DEFAULT_ITEM_ART: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frontmatter key inside the `hm3:` block that resolves a one-to-many row.
|
|
3
|
+
*
|
|
4
|
+
* One key for all four rows, deliberately: an author who has learned it on a
|
|
5
|
+
* weapon has learned it on a trauma. It is the block's `type` property — the
|
|
6
|
+
* document type this system compiles the note into — which is what the content
|
|
7
|
+
* format has always called it.
|
|
8
|
+
*
|
|
9
|
+
* @type {string}
|
|
10
|
+
*/
|
|
11
|
+
export const HM3_TYPE_KEY: string;
|
|
12
|
+
/**
|
|
13
|
+
* Every content type HM3 compiles into a Foundry document, and what it becomes.
|
|
14
|
+
*
|
|
15
|
+
* The Item rows are the nine types `item-builders.mjs` declares; the one Actor
|
|
16
|
+
* row is `being`, which HM3 splits into `character` and `creature`. Types this
|
|
17
|
+
* map does not name — `affiliation`, `affliction`, `attribute`,
|
|
18
|
+
* `concoction`, `mystery`, and every core type — compile into no HM3
|
|
19
|
+
* document at all, silently and correctly: HM3 has no form of them, and a
|
|
20
|
+
* finding on every such note would be the noise #79's rule exists to prevent.
|
|
21
|
+
*
|
|
22
|
+
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
23
|
+
*/
|
|
24
|
+
export const HM3_DOCUMENT_SUBTYPES: import("../engine/document-subtypes.mjs").DocumentSubtypeMap;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as itemBuilders from "./item-builders.mjs";
|
|
2
|
+
export * as itemFields from "./item-fields.mjs";
|
|
3
|
+
export * as documentSubtypes from "./document-subtypes.mjs";
|
|
4
|
+
export * as items from "./items.mjs";
|
|
5
|
+
export * as actors from "./actors.mjs";
|
|
6
|
+
export * as templatePriority from "./template-priority.mjs";
|
|
7
|
+
export { HM3_DEFAULT_ITEM_ART, hm3DefaultItemArt } from "./default-item-art.mjs";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every HM3 item type, paired with the builder for its `system` block, the
|
|
3
|
+
* default art for the type, and the frontmatter fields it declares.
|
|
4
|
+
*
|
|
5
|
+
* @type {Readonly<Record<string, Readonly<{system: (fm: object) => object, img: string, fields: readonly object[]}>>>}
|
|
6
|
+
*/
|
|
7
|
+
export const HM3_ITEM_BUILDERS: Readonly<Record<string, Readonly<{
|
|
8
|
+
system: (fm: object) => object;
|
|
9
|
+
img: string;
|
|
10
|
+
fields: readonly object[];
|
|
11
|
+
}>>>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every HM3 item type's frontmatter vocabulary, in the order the `system` block
|
|
3
|
+
* emits it.
|
|
4
|
+
*
|
|
5
|
+
* The keys are **note** types, not HM3 document subtypes — `projectile`
|
|
6
|
+
* rather than `missilegear`, `mysticalability` rather than `psionic` — because
|
|
7
|
+
* a registry is addressed by what a note calls itself. What the document is
|
|
8
|
+
* called is the map's answer, and only the map's.
|
|
9
|
+
*
|
|
10
|
+
* @type {Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>}
|
|
11
|
+
*/
|
|
12
|
+
export const HM3_ITEM_FIELDS: Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class Hm3Items extends SystemItemCompiler {
|
|
2
|
+
/**
|
|
3
|
+
* HM3's note-type → document-subtype map — the one declaration that says
|
|
4
|
+
* which block this pass reads, which notes it claims, and what each becomes
|
|
5
|
+
* (#58/#79).
|
|
6
|
+
*
|
|
7
|
+
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
8
|
+
*/
|
|
9
|
+
static documentSubtypes: import("../engine/document-subtypes.mjs").DocumentSubtypeMap;
|
|
10
|
+
/**
|
|
11
|
+
* The `system.*` field HM3 writes on an item from the note's prose.
|
|
12
|
+
*
|
|
13
|
+
* @param {object} fm - The note's frontmatter.
|
|
14
|
+
* @param {object} at - What the pass already knows about this note.
|
|
15
|
+
* @param {string} at.markdown - The note body, tables expanded and
|
|
16
|
+
* wikilinks resolved.
|
|
17
|
+
* @returns {object} The shared `system` fields — `description`, or nothing.
|
|
18
|
+
*/
|
|
19
|
+
commonSystem(fm: object, { markdown }: {
|
|
20
|
+
markdown: string;
|
|
21
|
+
}): object;
|
|
22
|
+
}
|
|
23
|
+
import { SystemItemCompiler } from "../engine/item-compiler.mjs";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A document's `flags`: whatever the note authors, plus this system's template
|
|
3
|
+
* priority.
|
|
4
|
+
*
|
|
5
|
+
* A note that is not a template — or states nothing — writes nothing, rather
|
|
6
|
+
* than a `null` nothing reads.
|
|
7
|
+
*
|
|
8
|
+
* **Read through the shared resolver, not a field declaration** (#266). A
|
|
9
|
+
* `FieldSpec`'s shared source is a single position, and this value has five:
|
|
10
|
+
* `data:`, this block, the top level, and the retiring `archetype` spelling in
|
|
11
|
+
* the latter two. The resolver is the single implementation of what a note said,
|
|
12
|
+
* so the two systems cannot disagree about it. It is read against **this**
|
|
13
|
+
* block: the `sohl:` block is not a source for an HM3 document, so a tree that
|
|
14
|
+
* still states the priority there (`harn-ensemble`, on 2,502 notes) writes no
|
|
15
|
+
* HM3 flag until it sweeps to `data:`.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} fm - The note's frontmatter.
|
|
18
|
+
* @param {string} block - This pass's system block.
|
|
19
|
+
* @returns {object} The flags to emit.
|
|
20
|
+
*/
|
|
21
|
+
export function templateFlags(fm: object, block: string): object;
|
package/types/manifest.d.mts
CHANGED
|
@@ -18,7 +18,7 @@ export function normalizeRepoUrl(repository: string | {
|
|
|
18
18
|
url?: string;
|
|
19
19
|
}): string;
|
|
20
20
|
/**
|
|
21
|
-
* The
|
|
21
|
+
* The addresses a Foundry manifest advertises.
|
|
22
22
|
*
|
|
23
23
|
* `manifest` deliberately points at **`releases/latest`** rather than at this
|
|
24
24
|
* version: it is the URL an *installed* package re-fetches to discover that a
|
|
@@ -42,6 +42,27 @@ export function releaseUrls({ repoUrl, version, artifact }: {
|
|
|
42
42
|
manifest: string;
|
|
43
43
|
download: string;
|
|
44
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Where this release publishes its content index (#239).
|
|
47
|
+
*
|
|
48
|
+
* **Pinned to this version, like `download` and unlike `manifest`.** A
|
|
49
|
+
* consumer reaches this URL by reading the dependency's manifest, so the
|
|
50
|
+
* manifest it read and the index it then fetches describe the same release —
|
|
51
|
+
* which is the whole point of publishing them together. A `releases/latest`
|
|
52
|
+
* index would silently pair a pinned manifest with a moving index, and the
|
|
53
|
+
* mismatch would surface as a cross-package link that resolved yesterday.
|
|
54
|
+
*
|
|
55
|
+
* @param {object} opts
|
|
56
|
+
* @param {string} opts.repoUrl - Normalised repository URL.
|
|
57
|
+
* @param {string} opts.version - The version being built.
|
|
58
|
+
* @param {string} opts.contentPackage - The content package name.
|
|
59
|
+
* @returns {string} The version-pinned asset URL.
|
|
60
|
+
*/
|
|
61
|
+
export function metadataUrl({ repoUrl, version, contentPackage }: {
|
|
62
|
+
repoUrl: string;
|
|
63
|
+
version: string;
|
|
64
|
+
contentPackage: string;
|
|
65
|
+
}): string;
|
|
45
66
|
/**
|
|
46
67
|
* The manifest's `packs`, derived from the one pack list the build already has.
|
|
47
68
|
*
|
package/types/release.d.mts
CHANGED
|
@@ -13,19 +13,24 @@
|
|
|
13
13
|
* @param {string} [opts.outDir] - Where the release assets are written.
|
|
14
14
|
* @param {"system"|"module"} [opts.artifact] - Which artifact is shipped.
|
|
15
15
|
* Determines both asset names.
|
|
16
|
-
* @
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* @param {string} [opts.metadataDir] - Where the build writes its content
|
|
17
|
+
* index, consulted when the advertised file was not staged.
|
|
18
|
+
* @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.
|
|
19
22
|
* @throws {Error} When the stage has no manifest — there is nothing to release,
|
|
20
23
|
* and an archive without one installs as nothing.
|
|
21
24
|
*/
|
|
22
|
-
export function packRelease({ stageDir, outDir, artifact, }?: {
|
|
25
|
+
export function packRelease({ stageDir, outDir, artifact, metadataDir, }?: {
|
|
23
26
|
stageDir?: string | undefined;
|
|
24
27
|
outDir?: string | undefined;
|
|
25
28
|
artifact?: "module" | "system" | undefined;
|
|
29
|
+
metadataDir?: string | undefined;
|
|
26
30
|
}): Promise<{
|
|
27
31
|
zip: string;
|
|
28
32
|
manifest: string;
|
|
33
|
+
metadata?: string;
|
|
29
34
|
bytes: number;
|
|
30
35
|
version: string;
|
|
31
36
|
}>;
|
package/types/sohl/actors.d.mts
CHANGED
|
@@ -1,80 +1,13 @@
|
|
|
1
|
-
export class Actors extends
|
|
1
|
+
export class Actors extends SystemActorCompiler {
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* embedded item reference's — reads one declaration that a subclass
|
|
8
|
-
* compiling for another system can replace. That is also what lets the
|
|
9
|
-
* non-identity behaviour be exercised without introducing a non-identity
|
|
10
|
-
* row into SoHL's own map, which is #78's job and moves compiled bytes.
|
|
3
|
+
* SoHL's note-type → document-subtype map — the one declaration that says
|
|
4
|
+
* which block this pass reads, which notes it claims, and what each
|
|
5
|
+
* becomes (#79). It is also what every embedded reference is translated
|
|
6
|
+
* through, which is why a subclass replaces one thing and not two.
|
|
11
7
|
*
|
|
12
8
|
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
13
9
|
*/
|
|
14
10
|
static documentSubtypes: import("../engine/document-subtypes.mjs").DocumentSubtypeMap;
|
|
15
|
-
constructor({ itemsSourceDirs, foreignSourceDirs, ...options }: {
|
|
16
|
-
[x: string]: any;
|
|
17
|
-
itemsSourceDirs?: never[] | undefined;
|
|
18
|
-
foreignSourceDirs?: never[] | undefined;
|
|
19
|
-
});
|
|
20
|
-
/** @type {readonly string[]} */
|
|
21
|
-
itemsSourceDirs: readonly string[];
|
|
22
|
-
foreignSourceDirs: any;
|
|
23
|
-
itemsMap: Map<string, object> | undefined;
|
|
24
|
-
/** @inheritdoc */
|
|
25
|
-
reportDetail(stats: any): void;
|
|
26
|
-
/**
|
|
27
|
-
* The Foundry Item subtype an embedded reference's `type` addresses.
|
|
28
|
-
*
|
|
29
|
-
* **The reference is in the note vocabulary; the address is in the
|
|
30
|
-
* document's** (#140). A being writes `(type, shortcode)` with the type an
|
|
31
|
-
* author authors, while {@link itemAddress} keys the predefined items by
|
|
32
|
-
* the subtype each compiled document carries — so exactly one of the two
|
|
33
|
-
* sides has to translate, and it is this one. The system's map is a
|
|
34
|
-
* function from note type to subtype by construction; the reverse is not,
|
|
35
|
-
* and a compiled document records nothing about the note that produced it,
|
|
36
|
-
* so there is no honest way to key the addresses the other way round.
|
|
37
|
-
*
|
|
38
|
-
* The two vocabularies are the same string in every SoHL row today, which
|
|
39
|
-
* is why looking a reference up verbatim worked. The first non-identity row
|
|
40
|
-
* (#78: `armor` → `armorgear`) ends that, and a reference resolving to
|
|
41
|
-
* nothing must be a finding rather than an item quietly missing from the
|
|
42
|
-
* compiled actor.
|
|
43
|
-
*
|
|
44
|
-
* @param {string} type - The type the reference names.
|
|
45
|
-
* @returns {import("../engine/document-subtypes.mjs").ReferencedSubtype}
|
|
46
|
-
* The subtype, or why the reference names none.
|
|
47
|
-
*/
|
|
48
|
-
embeddedSubtype(type: string): import("../engine/document-subtypes.mjs").ReferencedSubtype;
|
|
49
|
-
/**
|
|
50
|
-
* Resolve one embedded item from a `(type, shortcode?, overlay)`
|
|
51
|
-
* descriptor. If `shortcode` is given, the predefined item is fetched
|
|
52
|
-
* from `itemsMap` and the overlay deep-merged on top. If absent, the
|
|
53
|
-
* descriptor must carry enough fields to stand alone. The embedded
|
|
54
|
-
* item's `_id` is regenerated deterministically from
|
|
55
|
-
* `(actorId, subType, shortcode, indexKey)` so re-exports are stable —
|
|
56
|
-
* from the **document subtype**, so that renaming a note type (#78) leaves
|
|
57
|
-
* every embedded id exactly where it was.
|
|
58
|
-
* Returns null if the descriptor cannot be resolved.
|
|
59
|
-
*
|
|
60
|
-
* @param {Map<string, object>} itemsMap - The predefined items, by address.
|
|
61
|
-
* @param {string} actorId - The owning actor's id, seeding embedded ids.
|
|
62
|
-
* @param {string} type - The **note** type the reference names.
|
|
63
|
-
* @param {string|null} shortcode - The referenced item's shortcode, or
|
|
64
|
-
* `null` for a stand-alone entry.
|
|
65
|
-
* @param {object} [overlay] - The entry's remaining properties.
|
|
66
|
-
* @param {string} indexKey - Distinguishes two references to one item.
|
|
67
|
-
* @param {string} ctx - Diagnostic context (the actor's label).
|
|
68
|
-
* @param {object} [at] - Where to locate a finding.
|
|
69
|
-
* @param {string} [at.fmKey] - The frontmatter key the reference sits
|
|
70
|
-
* under, so an unresolved one is reported at the reference rather than
|
|
71
|
-
* at the note.
|
|
72
|
-
* @returns {object|null} The embedded item, or null when it resolved to
|
|
73
|
-
* nothing — always with a finding emitted.
|
|
74
|
-
*/
|
|
75
|
-
resolveEmbedded(itemsMap: Map<string, object>, actorId: string, type: string, shortcode: string | null, overlay?: object, indexKey: string, ctx: string, { fmKey }?: {
|
|
76
|
-
fmKey?: string | undefined;
|
|
77
|
-
}): object | null;
|
|
78
11
|
/**
|
|
79
12
|
* Build all embedded items for an actor: one per `sohl.attributes`
|
|
80
13
|
* entry plus one per `sohl.items` entry. `sohl.skills` is ignored.
|
|
@@ -114,7 +47,7 @@ export class Actors extends BasePackCompiler {
|
|
|
114
47
|
_id: any;
|
|
115
48
|
system: {
|
|
116
49
|
shortcode: any;
|
|
117
|
-
|
|
50
|
+
templatePriority: number | null;
|
|
118
51
|
portrait: string;
|
|
119
52
|
appearance: string;
|
|
120
53
|
dossier: string;
|
|
@@ -145,4 +78,4 @@ export class Actors extends BasePackCompiler {
|
|
|
145
78
|
_key: string;
|
|
146
79
|
};
|
|
147
80
|
}
|
|
148
|
-
import {
|
|
81
|
+
import { SystemActorCompiler } from "../engine/actor-compiler.mjs";
|
|
@@ -33,30 +33,6 @@ export function deriveBeingInfo(sohl: object | null | undefined, index: Map<stri
|
|
|
33
33
|
name?: string;
|
|
34
34
|
url?: string;
|
|
35
35
|
}>): object | null | undefined;
|
|
36
|
-
/**
|
|
37
|
-
* **A being's info-block fields**, derived from the items it embeds.
|
|
38
|
-
*
|
|
39
|
-
* A `being` note carries its embedded documents as `sohl.items` — a flat list
|
|
40
|
-
* of `{ shortcode, type, system? }` — but the shared theme's sidebar reads
|
|
41
|
-
* *resolved* shapes: a `skills` map, `gear` grouped by kind, and `spells` /
|
|
42
|
-
* `talents` split out of the mystical abilities. This is the translation
|
|
43
|
-
* between the two, and it is SoHL data-model knowledge: which item type is a
|
|
44
|
-
* skill, where a mastery level lives, what distinguishes a spell from a talent.
|
|
45
|
-
*
|
|
46
|
-
* **It lives here because it was living in two places.** Both
|
|
47
|
-
* `Song-of-Heroic-Lands-FoundryVTT` and `sohl-thalorna` carried a copy, and the
|
|
48
|
-
* copies drifted: SoHL's caller still gated the derivation on `character` and
|
|
49
|
-
* `creature`, the two types #1580 merged into `being`, so it had matched
|
|
50
|
-
* nothing since the merge and all 95 of its being pages published with empty
|
|
51
|
-
* sidebar sections (SoHL#1696). thalorna's copy checked `being` and was right.
|
|
52
|
-
* Nothing failed in either repository; the pages built and shipped.
|
|
53
|
-
*
|
|
54
|
-
* {@link isBeing} exists for that reason. The bug was not in the derivation —
|
|
55
|
-
* it was in each caller's idea of what a being *is*, written out per repository
|
|
56
|
-
* where it could rot independently. One definition, imported.
|
|
57
|
-
*
|
|
58
|
-
* @module
|
|
59
|
-
*/
|
|
60
36
|
/**
|
|
61
37
|
* The note `type` whose pages carry a being info block.
|
|
62
38
|
*
|
|
@@ -69,9 +45,14 @@ export const BEING_TYPE: "being";
|
|
|
69
45
|
/**
|
|
70
46
|
* The sidebar group each gear item type is displayed under.
|
|
71
47
|
*
|
|
72
|
-
* Presentation naming, not data-model naming: the
|
|
73
|
-
* sidebar heading says "weapons". Kept as one table so a new gear type is
|
|
74
|
-
* in a single place rather than in each consumer's site build.
|
|
48
|
+
* Presentation naming, not data-model naming: the note type says `weapongear`,
|
|
49
|
+
* the sidebar heading says "weapons". Kept as one table so a new gear type is
|
|
50
|
+
* added in a single place rather than in each consumer's site build.
|
|
51
|
+
*
|
|
52
|
+
* Keyed by **note** type, which is what a being's embedded `(type, shortcode)`
|
|
53
|
+
* references spell — and since #78 three of those are no longer the document
|
|
54
|
+
* subtype they compile into. A reference still on a renamed spelling is
|
|
55
|
+
* normalised at the lookup below rather than given a second row here.
|
|
75
56
|
*
|
|
76
57
|
* @type {Readonly<Record<string, string>>}
|
|
77
58
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The default art path for an item
|
|
2
|
+
* The default art path for an item **document subtype**, or throw when it is
|
|
3
|
+
* unknown —
|
|
3
4
|
* the build's fail-fast contract, so an unrecognized type is never silently
|
|
4
5
|
* defaulted (aborting the pack build rather than shipping a mismatched icon).
|
|
5
6
|
*
|
|
@@ -7,8 +8,9 @@
|
|
|
7
8
|
* {@link DEFAULT_ITEM_ART} directly and fall back to Foundry's default instead
|
|
8
9
|
* of calling this — see `SohlItem.getDefaultArtwork`.
|
|
9
10
|
*
|
|
10
|
-
* @param {string} type -
|
|
11
|
-
*
|
|
11
|
+
* @param {string} type - The SoHL Item document subtype — `armorgear`, not the
|
|
12
|
+
* `armor` note type that compiles into one.
|
|
13
|
+
* @returns {string} the default image path for that subtype.
|
|
12
14
|
*/
|
|
13
15
|
export function defaultItemArt(type: string): string;
|
|
14
16
|
export namespace DEFAULT_ITEM_ART {
|
package/types/sohl/items.d.mts
CHANGED
|
@@ -1,40 +1,25 @@
|
|
|
1
|
-
export class Items extends
|
|
1
|
+
export class Items extends SystemItemCompiler {
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* SoHL's note-type → document-subtype map — the one declaration that says
|
|
4
|
+
* which block this pass reads, which notes it claims, and what each becomes
|
|
5
|
+
* (#58/#79).
|
|
6
6
|
*
|
|
7
|
-
* @type {
|
|
7
|
+
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
static documentSubtypes: import("../engine/document-subtypes.mjs").DocumentSubtypeMap;
|
|
10
10
|
/**
|
|
11
|
-
* The
|
|
12
|
-
*
|
|
13
|
-
* **Looked up, not inferred.** For every type this system declares, the
|
|
14
|
-
* emitted subtype is the map's, so the note vocabulary and the document
|
|
15
|
-
* vocabulary are two separately-stated things rather than one string
|
|
16
|
-
* written twice (#79).
|
|
17
|
-
*
|
|
18
|
-
* **A type the map does not name belongs to the consumer**, and its
|
|
19
|
-
* registry entry is the declaration: a repository shipping an item type of
|
|
20
|
-
* its own writes it once, in the `itemBuilders` table of its
|
|
21
|
-
* `package-build.config.yaml`, and that key is what the document is a
|
|
22
|
-
* subtype of. That is an authored statement in the consumer's own
|
|
23
|
-
* configuration, not a coincidence inside this package's source — and
|
|
24
|
-
* refusing it here would silently drop every document of a type SoHL has
|
|
25
|
-
* no opinion about (#7/#1563).
|
|
11
|
+
* The `system.*` fields SoHL writes on every item, whatever its type:
|
|
12
|
+
* shortcode, templatePriority, actionDefs, notes, docHtml.
|
|
26
13
|
*
|
|
27
14
|
* @param {object} fm - The note's frontmatter.
|
|
28
|
-
* @
|
|
15
|
+
* @param {object} at - What the pass already knows about this note.
|
|
16
|
+
* @param {string} at.description - The pointer to the note's item doc.
|
|
17
|
+
* @param {string} at.label - Human-readable context for error messages.
|
|
18
|
+
* @returns {object} The shared `system` fields.
|
|
29
19
|
*/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
onCompiled(fm: any): void;
|
|
35
|
-
/** @inheritdoc */
|
|
36
|
-
reportCompiled(stats: any): void;
|
|
37
|
-
/** @inheritdoc */
|
|
38
|
-
reportDetail(stats: any): void;
|
|
20
|
+
commonSystem(fm: object, { description, label }: {
|
|
21
|
+
description: string;
|
|
22
|
+
label: string;
|
|
23
|
+
}): object;
|
|
39
24
|
}
|
|
40
|
-
import {
|
|
25
|
+
import { SystemItemCompiler } from "../engine/item-compiler.mjs";
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
-
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
-
*
|
|
5
|
-
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
-
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
-
*
|
|
8
|
-
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
-
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
-
*
|
|
11
|
-
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Whether a vendored manifest can still be *addressed*, as distinct from read.
|
|
16
|
-
*
|
|
17
|
-
* A consuming build vendors each foreign package's link manifest and resolves
|
|
18
|
-
* cross-package links through it. The lookup is by canonical key, so it needs
|
|
19
|
-
* both sides to agree on the key's shape: when they agree the link resolves,
|
|
20
|
-
* and when they drift apart the lookup cannot match on *any* input — while the
|
|
21
|
-
* page still reads correctly, because an unresolved wikilink falls through to
|
|
22
|
-
* its own display text. That is how a v3 key change left one repository reading
|
|
23
|
-
* 2,367 entries through a lookup that could never hit one of them (#1499).
|
|
24
|
-
*
|
|
25
|
-
* Bridging the lookup is not enough on its own: it fails silently again the next
|
|
26
|
-
* time either side moves. So the shapes are *checked* rather than merely
|
|
27
|
-
* converted, and a manifest that yields no addressable key at all fails the
|
|
28
|
-
* build. A lookup that cannot match anything reports nothing, which is the one
|
|
29
|
-
* failure a dead-link check can never catch.
|
|
30
|
-
*
|
|
31
|
-
* This guards {@link kbManifest}'s own key format, which is why it lives beside
|
|
32
|
-
* it rather than in whichever consumer happens to load a manifest.
|
|
33
|
-
*
|
|
34
|
-
* @module
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
import fs from "node:fs";
|
|
38
|
-
import path from "node:path";
|
|
39
|
-
|
|
40
|
-
import { formatDiagnostic, positionOfLiteral } from "./diagnostics.mjs";
|
|
41
|
-
import { readCanonicalKey } from "./kb-manifest.mjs";
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Every foreign package whose manifest entries a build cannot address.
|
|
45
|
-
*
|
|
46
|
-
* A package is reported only when it contributes entries and **none** of them
|
|
47
|
-
* yields a readable canonical key — the total, silent failure described above.
|
|
48
|
-
* Partial drift is deliberately not reported here: it resolves something, and
|
|
49
|
-
* whatever it fails to resolve surfaces as an ordinary dead address, pointed at
|
|
50
|
-
* the note that cites it. A package contributing no entries at all is likewise
|
|
51
|
-
* not a finding; a pack-only package publishes no addressable pages by design
|
|
52
|
-
* (#1516), and one being brought up publishes nothing yet.
|
|
53
|
-
*
|
|
54
|
-
* @param {Map<string, {package?: string}>} foreignIndex - `foreign.index` as
|
|
55
|
-
* returned by `loadForeignManifests`, keyed by canonical key.
|
|
56
|
-
* @returns {Array<{package: string, entries: number, sampleKey: string}>} One
|
|
57
|
-
* finding per drifted package, in the order the index first names each.
|
|
58
|
-
*/
|
|
59
|
-
export function unaddressableForeignPackages(foreignIndex) {
|
|
60
|
-
const byPackage = new Map();
|
|
61
|
-
for (const [key, value] of foreignIndex ?? new Map()) {
|
|
62
|
-
// The package is read from the entry rather than the key, since the key
|
|
63
|
-
// is the very thing under suspicion — deriving it from a shape that may
|
|
64
|
-
// not parse would report the finding against `undefined`.
|
|
65
|
-
const pkg = value?.package;
|
|
66
|
-
if (!pkg) continue;
|
|
67
|
-
const seen = byPackage.get(pkg) ?? {
|
|
68
|
-
entries: 0,
|
|
69
|
-
readable: 0,
|
|
70
|
-
sampleKey: key,
|
|
71
|
-
};
|
|
72
|
-
seen.entries += 1;
|
|
73
|
-
if (readCanonicalKey(key)) seen.readable += 1;
|
|
74
|
-
byPackage.set(pkg, seen);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const findings = [];
|
|
78
|
-
for (const [pkg, seen] of byPackage) {
|
|
79
|
-
if (seen.entries > 0 && seen.readable === 0) {
|
|
80
|
-
findings.push({
|
|
81
|
-
package: pkg,
|
|
82
|
-
entries: seen.entries,
|
|
83
|
-
sampleKey: seen.sampleKey,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return findings;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* One finding, in the standard `file:line:column: severity: message` form.
|
|
92
|
-
*
|
|
93
|
-
* The position is recovered by locating the offending key in the manifest text:
|
|
94
|
-
* the finding is about a literal the reader can see in the file, so its position
|
|
95
|
-
* is implicit rather than absent. When the file cannot be read, or the key is
|
|
96
|
-
* not in it, the locator degrades to the file alone — a dropped field, never a
|
|
97
|
-
* guessed `1:1` that would send the reader to the top of a 500 KB manifest for a
|
|
98
|
-
* finding that is not there.
|
|
99
|
-
*
|
|
100
|
-
* @param {{package: string, entries: number, sampleKey: string}} finding - One
|
|
101
|
-
* finding from {@link unaddressableForeignPackages}.
|
|
102
|
-
* @param {string} manifestDir - The directory the manifests were loaded from.
|
|
103
|
-
* @returns {string} The formatted diagnostic, path first on the line.
|
|
104
|
-
*/
|
|
105
|
-
export function formatUnaddressableFinding(finding, manifestDir) {
|
|
106
|
-
const file = path.join(manifestDir, `${finding.package}.json`);
|
|
107
|
-
let at = {};
|
|
108
|
-
try {
|
|
109
|
-
at = positionOfLiteral(fs.readFileSync(file, "utf8"), `"${finding.sampleKey}"`);
|
|
110
|
-
} catch {
|
|
111
|
-
// Unreadable here is not itself the finding — `loadForeignManifests`
|
|
112
|
-
// already reports that as a stale manifest. The file is simply all that
|
|
113
|
-
// is known about where this one is.
|
|
114
|
-
}
|
|
115
|
-
return formatDiagnostic({
|
|
116
|
-
file,
|
|
117
|
-
...at,
|
|
118
|
-
severity: "error",
|
|
119
|
-
message:
|
|
120
|
-
"no key in this manifest is a canonical " +
|
|
121
|
-
`\`package-type-shortcode\` address (${finding.entries} ` +
|
|
122
|
-
`${finding.entries === 1 ? "entry" : "entries"}, none addressable; ` +
|
|
123
|
-
`first is \`${finding.sampleKey}\`) — every cross-package link to ` +
|
|
124
|
-
`${finding.package} would resolve to nothing, silently`,
|
|
125
|
-
});
|
|
126
|
-
}
|