@heroiclands/package-build 20.3.1 → 20.4.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 +75 -0
- package/CONTENT.md +111 -92
- package/README.md +2 -9
- package/bin/content-build.mjs +70 -61
- package/bin/package-build.mjs +2 -2
- package/config.mjs +6 -8
- package/content-config.mjs +187 -64
- package/docs/content-format.md +57 -68
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +36 -36
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +12 -11
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +36 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +8 -8
- package/engine/code-fences.mjs +3 -3
- package/engine/compendiums.mjs +8 -8
- package/engine/compile-corpus.mjs +4 -5
- package/engine/content-address.mjs +15 -15
- package/engine/content-charset.mjs +1 -1
- package/engine/content-format-check.mjs +24 -25
- package/engine/content-format.mjs +8 -8
- package/engine/content-html.mjs +202 -0
- package/engine/content-icons.mjs +196 -165
- package/engine/content-index.mjs +17 -17
- package/engine/content-links.mjs +30 -30
- package/engine/content-lint.mjs +20 -24
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +7 -8
- package/engine/content-tree.mjs +1 -1
- package/engine/derived-fields.mjs +174 -0
- package/engine/diagnostics.mjs +3 -4
- package/engine/document-subtypes.mjs +6 -7
- package/engine/field-reference.mjs +8 -8
- package/engine/field-spec.mjs +29 -30
- package/engine/folder-notes.mjs +14 -14
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +64 -69
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +39 -39
- package/engine/helpers.mjs +63 -54
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +14 -8
- package/engine/item-compiler.mjs +54 -29
- package/engine/item-docs.mjs +11 -13
- package/engine/item-registry.mjs +13 -13
- package/engine/journals.mjs +15 -15
- package/engine/macros.mjs +3 -3
- package/engine/map-notes.mjs +6 -6
- package/engine/metadata-index.mjs +5 -5
- package/engine/note-claims.mjs +36 -34
- package/engine/note-ids.mjs +5 -5
- package/engine/note-package.mjs +4 -4
- package/engine/note-renames.mjs +3 -3
- package/engine/note-schemas.mjs +3 -3
- package/engine/note-vocabulary.mjs +27 -26
- package/engine/pack-config.mjs +13 -13
- package/engine/pack-router.mjs +0 -0
- package/engine/prose-config.mjs +9 -9
- package/engine/prose-lint.mjs +12 -13
- package/engine/region-events.mjs +2 -2
- package/engine/retired-fields.mjs +24 -22
- package/engine/runtime-only-fields.mjs +1 -1
- package/engine/scene-levels.mjs +1 -1
- package/engine/scenes.mjs +19 -20
- package/engine/schema-check.mjs +20 -20
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +45 -47
- package/engine/site-index.mjs +17 -18
- package/engine/sql-tables.mjs +21 -13
- package/engine/subtype-registry.mjs +5 -4
- package/engine/system-block.mjs +18 -19
- package/engine/systems.mjs +2 -2
- package/engine/web-wikilinks.mjs +35 -35
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +39 -39
- package/hm3/actors.mjs +22 -22
- package/hm3/document-subtypes.mjs +5 -5
- package/hm3/index.mjs +2 -2
- package/hm3/item-builders.mjs +1 -1
- package/hm3/item-fields.mjs +3 -3
- package/hm3/items.mjs +20 -8
- package/hm3/template-priority.mjs +2 -2
- package/lang.mjs +3 -3
- package/manifest.mjs +17 -20
- package/package.json +1 -2
- package/release.mjs +3 -3
- package/sohl/actors.mjs +28 -28
- package/sohl/affiliation-standings.mjs +2 -2
- package/sohl/being-info.mjs +5 -5
- package/sohl/default-item-art.mjs +5 -5
- package/sohl/document-subtypes.mjs +5 -5
- package/sohl/index.mjs +3 -3
- package/sohl/item-builders.mjs +7 -7
- package/sohl/item-fields.mjs +8 -9
- package/sohl/items.mjs +20 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +7 -7
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +25 -24
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +14 -13
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +3 -3
- package/types/engine/base-compiler.d.mts +17 -19
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +1 -1
- package/types/engine/code-fences.d.mts +3 -3
- package/types/engine/compendiums.d.mts +3 -3
- package/types/engine/compile-corpus.d.mts +1 -1
- package/types/engine/content-address.d.mts +11 -11
- package/types/engine/content-format-check.d.mts +6 -6
- package/types/engine/content-format.d.mts +2 -2
- package/types/engine/content-html.d.mts +78 -0
- package/types/engine/content-icons.d.mts +139 -92
- package/types/engine/content-index.d.mts +2 -2
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +2 -2
- package/types/engine/content-package.d.mts +6 -8
- package/types/engine/derived-fields.d.mts +101 -0
- package/types/engine/diagnostics.d.mts +2 -2
- package/types/engine/document-subtypes.d.mts +3 -3
- package/types/engine/field-spec.d.mts +37 -39
- package/types/engine/folder-notes.d.mts +4 -5
- package/types/engine/foreign-catalog.d.mts +3 -3
- package/types/engine/foundry-entries.d.mts +6 -7
- package/types/engine/frontmatter-lint.d.mts +7 -7
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +25 -27
- package/types/engine/homepage.d.mts +16 -18
- package/types/engine/ids.d.mts +13 -13
- package/types/engine/index-records.d.mts +3 -3
- package/types/engine/index.d.mts +2 -0
- package/types/engine/item-compiler.d.mts +21 -5
- package/types/engine/item-docs.d.mts +2 -2
- package/types/engine/item-registry.d.mts +6 -6
- package/types/engine/journals.d.mts +3 -3
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/metadata-index.d.mts +4 -4
- package/types/engine/note-claims.d.mts +16 -15
- package/types/engine/note-ids.d.mts +4 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-renames.d.mts +3 -3
- package/types/engine/note-vocabulary.d.mts +9 -8
- package/types/engine/pack-config.d.mts +3 -3
- package/types/engine/pack-router.d.mts +1 -1
- package/types/engine/prose-config.d.mts +9 -9
- package/types/engine/prose-lint.d.mts +3 -4
- package/types/engine/region-events.d.mts +2 -2
- package/types/engine/retired-fields.d.mts +10 -9
- package/types/engine/schema-check.d.mts +11 -11
- package/types/engine/site-build.d.mts +18 -19
- package/types/engine/site-index.d.mts +3 -4
- package/types/engine/sql-tables.d.mts +11 -5
- package/types/engine/subtype-registry.d.mts +3 -3
- package/types/engine/system-block.d.mts +3 -3
- package/types/engine/web-wikilinks.d.mts +7 -7
- package/types/engine/wikilink-syntax.d.mts +17 -17
- package/types/engine/wikilinks.d.mts +13 -12
- package/types/hm3/actors.d.mts +1 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +1 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/affiliation-standings.d.mts +2 -2
- package/types/sohl/being-info.d.mts +2 -2
- package/types/sohl/document-subtypes.d.mts +1 -1
- package/types/sohl/items.d.mts +1 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
package/manifest.mjs
CHANGED
|
@@ -18,16 +18,13 @@
|
|
|
18
18
|
* so there is one job here with two spellings: assemble the manifest from the
|
|
19
19
|
* repository's configuration and write it into the build stage.
|
|
20
20
|
*
|
|
21
|
-
* **There is no template
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* nothing checking that the pairs agreed. `sohl-kethira-basic` hand-maintained
|
|
27
|
-
* its whole `module.json`, and its `download` named an older version than the
|
|
28
|
-
* module claimed.
|
|
21
|
+
* **There is no template.** A `system.template.json` this module stamped a few
|
|
22
|
+
* fields into would be the one build input written as JSON, by hand, per
|
|
23
|
+
* repository, with no schema and nothing checking it — and it would declare
|
|
24
|
+
* facts the configuration also declares, the pack list twice in two formats,
|
|
25
|
+
* with nothing checking that the pairs agree.
|
|
29
26
|
*
|
|
30
|
-
* So the manifest is generated
|
|
27
|
+
* So the manifest is generated. Three kinds of key end up in it:
|
|
31
28
|
*
|
|
32
29
|
* - **Declared** — the `packageBuild.manifest` block, emitted unchanged, so a
|
|
33
30
|
* key Foundry adds in a later version needs no release of this package.
|
|
@@ -122,7 +119,7 @@ export function releaseUrls({ repoUrl, version, artifact }) {
|
|
|
122
119
|
}
|
|
123
120
|
|
|
124
121
|
/**
|
|
125
|
-
* Where this release publishes its content index
|
|
122
|
+
* Where this release publishes its content index.
|
|
126
123
|
*
|
|
127
124
|
* **Pinned to this version, like `download` and unlike `manifest`.** A
|
|
128
125
|
* consumer reaches this URL by reading the dependency's manifest, so the
|
|
@@ -183,7 +180,7 @@ const MANIFEST_KEY_ORDER = Object.freeze([
|
|
|
183
180
|
/**
|
|
184
181
|
* The manifest's `packs`, derived from the one pack list the build already has.
|
|
185
182
|
*
|
|
186
|
-
* The two
|
|
183
|
+
* The two are not written separately, with `package-build.config.yaml` declaring
|
|
187
184
|
* a pack's name and type, and the manifest template declared them again beside
|
|
188
185
|
* a label, a path and a system id, with nothing checking that the pairs agreed.
|
|
189
186
|
* They are one list now.
|
|
@@ -265,12 +262,12 @@ function namedPacks(folders, at) {
|
|
|
265
262
|
* `styles`, `languages`), and a staged file is a different relation, checked
|
|
266
263
|
* against the stage rather than against configuration. So this is the one place
|
|
267
264
|
* a declaration can go stale against a value the build already computed — and
|
|
268
|
-
* until now nothing compared them
|
|
265
|
+
* until now nothing compared them.
|
|
269
266
|
*
|
|
270
267
|
* `HarnMaster-3-FoundryVTT` shipped the consequence: its folder named four
|
|
271
268
|
* packs, three of which had not existed since the compendium was consolidated,
|
|
272
269
|
* and omitted `items` — 1,577 of 1,597 documents, loose in Foundry's compendium
|
|
273
|
-
* browser, with the build reporting nothing
|
|
270
|
+
* browser, with the build reporting nothing.
|
|
274
271
|
*
|
|
275
272
|
* **The two findings are not the same finding**, and giving them one severity
|
|
276
273
|
* gets one of them wrong:
|
|
@@ -343,14 +340,14 @@ export function packFolderFindings({ packFolders, packs = [] }) {
|
|
|
343
340
|
*
|
|
344
341
|
* `relationships` is the one manifest block with a second reader.
|
|
345
342
|
* `@heroiclands/content-build` consumes it too, and v1.8.0 added
|
|
346
|
-
* `itemCatalog: true` as an opt-in on a declared dependency
|
|
347
|
-
*
|
|
343
|
+
* `itemCatalog: true` as an opt-in on a declared dependency: it selects that
|
|
344
|
+
* package's Item packs as a resolution
|
|
348
345
|
* source for the actors pass. That is an instruction to the build, not a fact
|
|
349
346
|
* about the shipped package — Foundry's relationship schema does not define
|
|
350
347
|
* it, and someone reading a published manifest cannot tell a build directive
|
|
351
348
|
* from a declaration about what the package needs.
|
|
352
349
|
*
|
|
353
|
-
* So the block is filtered rather than copied whole
|
|
350
|
+
* So the block is filtered rather than copied whole. The rule is the
|
|
354
351
|
* distinction, not the name: a key listed here answers *how is this built?*,
|
|
355
352
|
* and every key that survives answers *what does this package depend on?*.
|
|
356
353
|
* `itemCatalog` is the first build-time key to land on a relationship and is
|
|
@@ -441,7 +438,7 @@ export function buildManifest({ config, packageJson, artifact, flags }) {
|
|
|
441
438
|
};
|
|
442
439
|
if (config.compatibility) derived.compatibility = config.compatibility;
|
|
443
440
|
|
|
444
|
-
// `requiresSystem` is the gate half of the declare/require split
|
|
441
|
+
// `requiresSystem` is the gate half of the declare/require split. It
|
|
445
442
|
// emits the `relationships.systems` entry Foundry's `supportsSystem` reads,
|
|
446
443
|
// reusing the `systems:` declaration rather than restating it — a second
|
|
447
444
|
// transcription is free to disagree with what it copied, which is how
|
|
@@ -476,8 +473,8 @@ export function buildManifest({ config, packageJson, artifact, flags }) {
|
|
|
476
473
|
|
|
477
474
|
const merged = { ...declared, ...derived };
|
|
478
475
|
|
|
479
|
-
// The index every consumer resolves this package's addresses through
|
|
480
|
-
//
|
|
476
|
+
// The index every consumer resolves this package's addresses through.
|
|
477
|
+
// Written unconditionally, because a package that publishes no
|
|
481
478
|
// index is one nothing can link into — and the failure of an absent one is
|
|
482
479
|
// a dead link in somebody else's build, which is exactly the kind of
|
|
483
480
|
// silence this replaced the vendored manifest to end.
|
|
@@ -562,7 +559,7 @@ function reportPackFolders(findings, configFile) {
|
|
|
562
559
|
* The declared `packFolders` is checked against the derived `packs[]` first,
|
|
563
560
|
* and an unresolvable name **stops the write**: a manifest already known to
|
|
564
561
|
* describe packs the package does not ship should not reach the stage, where
|
|
565
|
-
* the next command would deploy it
|
|
562
|
+
* the next command would deploy it. See {@link packFolderFindings} for
|
|
566
563
|
* the rule and why its two findings carry different severities.
|
|
567
564
|
*
|
|
568
565
|
* @param {object} options - As {@link buildManifest}, plus where to write.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "20.
|
|
3
|
+
"version": "20.4.0",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -101,7 +101,6 @@
|
|
|
101
101
|
"CHANGELOG-content-build.md",
|
|
102
102
|
"CHANGELOG.md",
|
|
103
103
|
"CONTENT.md",
|
|
104
|
-
"MIGRATING.md",
|
|
105
104
|
"README.md",
|
|
106
105
|
"bin",
|
|
107
106
|
"bundle.mjs",
|
package/release.mjs
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* advertises, so a release publishes `<artifact>.zip`, the whole staged tree,
|
|
20
20
|
* and `<artifact>.json` beside it, which is what an already installed package
|
|
21
21
|
* re-fetches to notice a new version. A package that ships content publishes a
|
|
22
|
-
* third: the content index other packages resolve its addresses through
|
|
22
|
+
* third: the content index other packages resolve its addresses through,
|
|
23
23
|
* named by the `flags.metadataUrl` the manifest advertises. Every name is fixed
|
|
24
24
|
* by what the manifest says, not chosen here — see `manifest.mjs`.
|
|
25
25
|
*
|
|
@@ -37,7 +37,7 @@ import path from "node:path";
|
|
|
37
37
|
// archiver 8 is pure ESM and exports **classes**, with no default export. The
|
|
38
38
|
// old `import archiver from "archiver"` factory call throws at import —
|
|
39
39
|
// `does not provide an export named 'default'` — which is how this repository's
|
|
40
|
-
// release job came to fail before a single byte was written
|
|
40
|
+
// release job came to fail before a single byte was written.
|
|
41
41
|
import { ZipArchive } from "archiver";
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -122,7 +122,7 @@ export async function packRelease({
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
* Place the content index the manifest advertises beside the archive
|
|
125
|
+
* Place the content index the manifest advertises beside the archive.
|
|
126
126
|
*
|
|
127
127
|
* **The asset's name comes from the manifest, not from here.** `flags.metadataUrl`
|
|
128
128
|
* is the URL every consumer fetches, so its basename is by definition the name
|
package/sohl/actors.mjs
CHANGED
|
@@ -17,21 +17,21 @@
|
|
|
17
17
|
* The machinery an actor of any system needs — the predefined-item catalogue,
|
|
18
18
|
* reference translation through the system's map, embedding and its stable ids,
|
|
19
19
|
* the anchored prose sections — lives in
|
|
20
|
-
* {@link module:engine/actor-compiler}, where a second system reaches it
|
|
21
|
-
*
|
|
20
|
+
* {@link module:engine/actor-compiler}, where a second system reaches it.
|
|
21
|
+
* What is left here is SoHL's data model: the body structure and its
|
|
22
22
|
* movement profiles, the attributes-and-items frontmatter that becomes embedded
|
|
23
23
|
* documents, the opening mastery level a skill is baked with, and the `system`
|
|
24
24
|
* block itself.
|
|
25
25
|
*
|
|
26
26
|
* One content type today, and the actor subtype it produces is **declared**
|
|
27
27
|
* rather than assumed to be the same word: `sohl/document-subtypes.mjs` maps
|
|
28
|
-
* `being` → `Actor` / `being`, and this pass looks it up
|
|
28
|
+
* `being` → `Actor` / `being`, and this pass looks it up. It read
|
|
29
29
|
* `ACTOR_VAULT_TYPE = "being"` here and emitted `type: "being"` several hundred
|
|
30
30
|
* lines below, which made the two vocabularies agree by coincidence.
|
|
31
31
|
*
|
|
32
32
|
* It was two content types — `character` and `creature` — which compiled to the
|
|
33
33
|
* same `being` with no branch anywhere between them; they were retired in
|
|
34
|
-
*
|
|
34
|
+
* retired, and are reported by `assertTypeNotRetired` in
|
|
35
35
|
* `engine/ids.mjs`.
|
|
36
36
|
*
|
|
37
37
|
* Attributes (`sohl.attributes` map) become embedded attribute items with
|
|
@@ -56,16 +56,16 @@ import {
|
|
|
56
56
|
import { openingMasteryLevel } from "./skill-base.mjs";
|
|
57
57
|
import { SystemActorCompiler, renderSection } from "../engine/actor-compiler.mjs";
|
|
58
58
|
// Which Foundry Actor subtype a note's `type` compiles into. Looked up in the
|
|
59
|
-
// system's declared map, never inferred from the type itself
|
|
59
|
+
// system's declared map, never inferred from the type itself.
|
|
60
60
|
import { documentSubtype } from "../engine/document-subtypes.mjs";
|
|
61
61
|
import { SOHL_DOCUMENT_SUBTYPES } from "./document-subtypes.mjs";
|
|
62
62
|
// The note-level `sohl:` block: `sohl.system` onto the document's `system`
|
|
63
63
|
// verbatim, and `sohl.img` / `sohl.effects` / `sohl.flags` overriding their
|
|
64
|
-
// shared top-level forms for this system alone
|
|
64
|
+
// shared top-level forms for this system alone.
|
|
65
65
|
import { blockProperty, mergeSystemData } from "../engine/system-block.mjs";
|
|
66
66
|
import { readField, retiredTopLevelKey } from "../engine/field-spec.mjs";
|
|
67
67
|
// The retirement window's reports, shared with the frontmatter lint so the two
|
|
68
|
-
// cannot say different things about the same key
|
|
68
|
+
// cannot say different things about the same key.
|
|
69
69
|
import {
|
|
70
70
|
legacyKeyMessage,
|
|
71
71
|
locateFrontmatterKey,
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
* The system this pass compiles for — the block its notes write.
|
|
77
77
|
*
|
|
78
78
|
* Read from the map rather than spelled here, so the block name and the subtype
|
|
79
|
-
* map are one statement
|
|
79
|
+
* map are one statement.
|
|
80
80
|
*
|
|
81
81
|
* @type {string}
|
|
82
82
|
*/
|
|
@@ -98,7 +98,7 @@ const DEFAULT_IMG = {
|
|
|
98
98
|
* and the note's top level and nothing else — so `data.portrait`, the position
|
|
99
99
|
* the specification names and `sohl-thalorna` writes on 646 beings, was
|
|
100
100
|
* invisible, and `?? defaultImg` on the next line turned every miss into the
|
|
101
|
-
* generic person icon rather than into a complaint
|
|
101
|
+
* generic person icon rather than into a complaint. Going through
|
|
102
102
|
* {@link module:engine/field-spec.readField} is what makes the mapping table
|
|
103
103
|
* executable here as it already is for HM3's `data.species`.
|
|
104
104
|
*
|
|
@@ -115,7 +115,7 @@ const PORTRAIT_FIELD = Object.freeze({
|
|
|
115
115
|
shape: "path",
|
|
116
116
|
// The two empties survive, because the caller's `?? defaultImg` is what
|
|
117
117
|
// tells them apart: `null` and an absent key mean "no art named, default
|
|
118
|
-
// me", `""` means "ship blank on purpose"
|
|
118
|
+
// me", `""` means "ship blank on purpose".
|
|
119
119
|
read: (raw) => resolveImg(raw),
|
|
120
120
|
default: null,
|
|
121
121
|
describe: "Path to the portrait image.",
|
|
@@ -194,7 +194,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
194
194
|
/**
|
|
195
195
|
* SoHL's note-type → document-subtype map — the one declaration that says
|
|
196
196
|
* which block this pass reads, which notes it claims, and what each
|
|
197
|
-
* becomes
|
|
197
|
+
* becomes. It is also what every embedded reference is translated
|
|
198
198
|
* through, which is why a subclass replaces one thing and not two.
|
|
199
199
|
*
|
|
200
200
|
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
@@ -298,7 +298,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
/**
|
|
301
|
-
* Bake each unopened skill's opening mastery level into the document
|
|
301
|
+
* Bake each unopened skill's opening mastery level into the document.
|
|
302
302
|
*
|
|
303
303
|
* A skill whose `masteryLevelBase` is still null once the note's frontmatter
|
|
304
304
|
* has been merged onto the catalogue entry is *not yet opened*, and the
|
|
@@ -352,7 +352,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
352
352
|
const id = fm.id;
|
|
353
353
|
const ctx = `actor "${name}"`;
|
|
354
354
|
// The document's own subtype, and the art that goes with it. Both are
|
|
355
|
-
// looked up from the note's `type` rather than spelled here
|
|
355
|
+
// looked up from the note's `type` rather than spelled here.
|
|
356
356
|
const subType = /** @type {string} */ (
|
|
357
357
|
documentSubtype(this.constructor.documentSubtypes, fm.type, fm, {
|
|
358
358
|
absPath: this.currentNote?.absPath,
|
|
@@ -365,13 +365,13 @@ export class Actors extends SystemActorCompiler {
|
|
|
365
365
|
const { value: authoredFolder, isAddress } = folderField(fm);
|
|
366
366
|
const folder = this.folderResolver(authoredFolder, { isAddress });
|
|
367
367
|
|
|
368
|
-
// The two retiring positions a declared field may be read from
|
|
369
|
-
//
|
|
368
|
+
// The two retiring positions a declared field may be read from.
|
|
369
|
+
// **Warnings**, on the pattern every retirement in this package
|
|
370
370
|
// follows: the note compiles to the correct document either way, so
|
|
371
371
|
// reddening a tree over one would refuse before the sweep rather than
|
|
372
|
-
// after it. What they buy is a count —
|
|
373
|
-
//
|
|
374
|
-
//
|
|
372
|
+
// after it. What they buy is a count — without one, nothing says which
|
|
373
|
+
// position a value came from, and a default is indistinguishable from
|
|
374
|
+
// a miss.
|
|
375
375
|
const portraitReports = {
|
|
376
376
|
block: SYSTEM,
|
|
377
377
|
onLegacyKey: (field) =>
|
|
@@ -398,25 +398,25 @@ export class Actors extends SystemActorCompiler {
|
|
|
398
398
|
const system = {
|
|
399
399
|
// The frontmatter shortcode is the actor's stable `(type, shortcode)`
|
|
400
400
|
// key — and, for a being that is an archetype, its archetype
|
|
401
|
-
// identity (the dedup/override key of the Create-dialog picker
|
|
401
|
+
// identity (the dedup/override key of the Create-dialog picker).
|
|
402
402
|
shortcode: fm.shortcode || "",
|
|
403
403
|
// Required nullable number: a priority, or `null` for a being that
|
|
404
|
-
// is not a template (
|
|
405
|
-
//
|
|
404
|
+
// is not a template (the archetype contract). `system.archetype`
|
|
405
|
+
// is the legacy spelling; the emitted key moves
|
|
406
406
|
// with the schema, since an undeclared `system` key is discarded at
|
|
407
407
|
// construction without a warning.
|
|
408
408
|
templatePriority: systemTemplatePriority(fm, ctx),
|
|
409
|
-
// Nullish, not
|
|
409
|
+
// Nullish, not `||`: a note that names no portrait gets the
|
|
410
410
|
// subtype's default, one that writes `""` ships blank on purpose.
|
|
411
411
|
// Resolved through the declaration so `data.portrait` is reached at
|
|
412
|
-
// all — see {@link PORTRAIT_FIELD}
|
|
412
|
+
// all — see {@link PORTRAIT_FIELD}.
|
|
413
413
|
portrait: readField(PORTRAIT_FIELD, fm, portraitReports) ?? defaultImg,
|
|
414
414
|
appearance: renderSection(body || "", "appearance"),
|
|
415
415
|
dossier: renderSection(body || "", "dossier"),
|
|
416
416
|
};
|
|
417
417
|
|
|
418
418
|
// Fill `system.body` (+ the base-actor movement fields) from the being's
|
|
419
|
-
// frontmatter, rather than embedding a corpus item
|
|
419
|
+
// frontmatter, rather than embedding a corpus item. The `sohl`
|
|
420
420
|
// block mirrors `system` field-for-field: `sohl.body` nests the body
|
|
421
421
|
// (`structure` / `weight` / …), with `currentMoveMedium` /
|
|
422
422
|
// `movementProfiles` flat alongside it. An **incorporeal** being omits
|
|
@@ -441,14 +441,14 @@ export class Actors extends SystemActorCompiler {
|
|
|
441
441
|
}
|
|
442
442
|
|
|
443
443
|
// Whatever the note authors under `sohl.system`, at the DataModel's own
|
|
444
|
-
// paths
|
|
444
|
+
// paths. This pass has no field declaration, so it claims
|
|
445
445
|
// nothing: every authored path is the author's, and the fields above
|
|
446
446
|
// are what a note that authors none still gets.
|
|
447
447
|
mergeSystemData(system, fm, { block: SYSTEM });
|
|
448
448
|
this.reportUndeclaredSystemData(fm, SYSTEM, "Actor", subType);
|
|
449
449
|
// And what this pass wrote itself. There is no field declaration for a
|
|
450
450
|
// being at all, so *every* key here is a compiler emission — including
|
|
451
|
-
// `templatePriority
|
|
451
|
+
// `templatePriority`, which nothing compared until.
|
|
452
452
|
this.reportEmittedSystemData(system, {
|
|
453
453
|
fm,
|
|
454
454
|
block: SYSTEM,
|
|
@@ -462,7 +462,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
462
462
|
return {
|
|
463
463
|
name,
|
|
464
464
|
type: subType,
|
|
465
|
-
// Nullish, not `||` — see the portrait above
|
|
465
|
+
// Nullish, not `||` — see the portrait above.
|
|
466
466
|
img: resolveImg(blockProperty(fm, SYSTEM, "img")) ?? defaultImg,
|
|
467
467
|
_id: id,
|
|
468
468
|
system,
|
|
@@ -483,7 +483,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
483
483
|
ownership: { default: 0 },
|
|
484
484
|
// Whatever the note authors, and nothing else. The priority used to
|
|
485
485
|
// be spliced in here as `flags.sohl.docArchetype`; it is a schema
|
|
486
|
-
// field now and sits in `system
|
|
486
|
+
// field now and sits in `system`.
|
|
487
487
|
flags: blockProperty(fm, SYSTEM, "flags", {}),
|
|
488
488
|
_stats: this.stats,
|
|
489
489
|
_key: `!actors!${id}`,
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The affiliation standings an authored `relation` map may use — one
|
|
16
|
-
* affiliation's stance toward another
|
|
16
|
+
* affiliation's stance toward another.
|
|
17
17
|
*
|
|
18
18
|
* Plain ESM for the same reason `./default-item-art.mjs` is: the pack scripts
|
|
19
19
|
* run under bare `node`, so they cannot read the runtime's TypeScript
|
|
20
20
|
* `AFFILIATION_STANDING`. This module is where the pipeline reads the list
|
|
21
|
-
* from, rather than restating it inside a compiler
|
|
21
|
+
* from, rather than restating it inside a compiler.
|
|
22
22
|
*
|
|
23
23
|
* The runtime keeps its own `defineType("SOHL.Affiliation.Standing", { … })`
|
|
24
24
|
* declaration in `src/utils/constants.ts`, because that literal is *parsed out
|
package/sohl/being-info.mjs
CHANGED
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
* **It lives here because it was living in two places.** Both
|
|
25
25
|
* `Song-of-Heroic-Lands-FoundryVTT` and `sohl-thalorna` carried a copy, and the
|
|
26
26
|
* copies drifted: SoHL's caller still gated the derivation on `character` and
|
|
27
|
-
* `creature`, the two
|
|
27
|
+
* `creature`, the two legacy spellings of `being`, so it had matched
|
|
28
28
|
* nothing since the merge and all 95 of its being pages published with empty
|
|
29
|
-
* sidebar sections
|
|
29
|
+
* sidebar sections. thalorna's copy checked `being` and was right.
|
|
30
30
|
* Nothing failed in either repository; the pages built and shipped.
|
|
31
31
|
*
|
|
32
32
|
* {@link isBeing} exists for that reason. The bug was not in the derivation —
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
* @module
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
|
-
// The retirement window for a renamed note type
|
|
39
|
+
// The retirement window for a renamed note type: an embedded reference
|
|
40
40
|
// still spelling `armorgear` names the same gear group as `armor`.
|
|
41
41
|
import { currentType } from "../engine/ids.mjs";
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* The note `type` whose pages carry a being info block.
|
|
45
45
|
*
|
|
46
|
-
* One name
|
|
46
|
+
* One name: `character` and `creature` are the `being` they
|
|
47
47
|
* had always compiled into. The retired names are deliberately **not** accepted
|
|
48
48
|
* as aliases: they throw elsewhere in the system, and tolerating them here
|
|
49
49
|
* would hide the next drift of this kind rather than surface it.
|
|
@@ -68,7 +68,7 @@ export function isBeing(fm) {
|
|
|
68
68
|
* added in a single place rather than in each consumer's site build.
|
|
69
69
|
*
|
|
70
70
|
* Keyed by **note** type, which is what a being's embedded `(type, shortcode)`
|
|
71
|
-
* references spell — and
|
|
71
|
+
* references spell — and three of those are not the document
|
|
72
72
|
* subtype they compile into. A reference still on a renamed spelling is
|
|
73
73
|
* normalised at the lookup below rather than given a second row here.
|
|
74
74
|
*
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
* runtime can both import it. A `.ts` module could not be required by the build
|
|
28
28
|
* scripts (they run under bare `node`, without the bundler that resolves the
|
|
29
29
|
* `@src` alias and strips types). Keeping one map here is what prevents the
|
|
30
|
-
* build-time and runtime defaults from drifting apart (
|
|
30
|
+
* build-time and runtime defaults from drifting apart (which is exactly
|
|
31
31
|
* that drift: the builder had a default, runtime did not).
|
|
32
32
|
*
|
|
33
33
|
* It lives in this package rather than in the system's `src/` tree because the
|
|
34
|
-
* pack pipeline is installed as a dependency
|
|
34
|
+
* pack pipeline is installed as a dependency: a relative path out of
|
|
35
35
|
* the package would resolve to garbage once the pipeline runs from
|
|
36
36
|
* `node_modules`. The runtime imports it back through the package's
|
|
37
|
-
* `./sohl/default-item-art` entry point, so there is still exactly one map
|
|
38
|
-
*
|
|
37
|
+
* `./sohl/default-item-art` entry point, so there is still exactly one map.
|
|
38
|
+
* Injecting the map through configuration instead would re-open that drift,
|
|
39
39
|
* because the two sides would once more be free to disagree.
|
|
40
40
|
*
|
|
41
41
|
* Paths are fully resolved (`systems/sohl/...`) — the served path both layers
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
* **The keys are SoHL *document* subtypes, not markdown note types.** The
|
|
45
45
|
* runtime reads this with `itemData.type`, which is a Foundry Item subtype, and
|
|
46
46
|
* that is the vocabulary a compendium document carries. The two vocabularies
|
|
47
|
-
*
|
|
47
|
+
* would be the same string but for three renamed note types (`armorgear` →
|
|
48
48
|
* `armor`, and its two siblings), and this map stayed on the document side
|
|
49
49
|
* because that is the side the runtime cannot translate: `sohl/item-builders.mjs`
|
|
50
50
|
* asks SoHL's own note-type → subtype map before it looks art up here, so the
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* **SoHL's note-type → document-subtype map** — which Foundry document, and
|
|
16
|
-
* which subtype of it, a note of each content type compiles into
|
|
16
|
+
* which subtype of it, a note of each content type compiles into.
|
|
17
17
|
*
|
|
18
18
|
* The mechanism is `engine/document-subtypes.mjs`; what a *game system* does
|
|
19
19
|
* with it is here, which is the `engine/` ÷ `sohl/` line this package draws
|
|
20
|
-
* everywhere else
|
|
20
|
+
* everywhere else. A second system declares its own map in its own half
|
|
21
21
|
* and shares nothing but the mechanism.
|
|
22
22
|
*
|
|
23
23
|
* **Every row is written out, identity rows included.** `skill → skill` looks
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* **Three rows are no longer the identity, and that is what the map was for.**
|
|
32
32
|
* `armor`, `concoction` and `projectile` were renamed from the `…gear`
|
|
33
33
|
* spellings that named SoHL's *document* subtype rather than the thing the note
|
|
34
|
-
* is about
|
|
34
|
+
* is about; the subtypes themselves did not move, so those three rows now
|
|
35
35
|
* say two different words where they used to say one twice. Landing the rename
|
|
36
36
|
* cost exactly what having the map first promised it would: a data change on
|
|
37
37
|
* three lines here, and no mechanism change anywhere.
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
* before this map is consulted — so a tree that has not swept its 30,741
|
|
41
41
|
* embedded `(type, shortcode)` references compiles byte-identically. `weapon`
|
|
42
42
|
* is deliberately **not** among them: both systems call that document a
|
|
43
|
-
* `weapongear`, so the name says nothing SoHL-specific and
|
|
43
|
+
* `weapongear`, so the name says nothing SoHL-specific and is left alone.
|
|
44
44
|
*
|
|
45
45
|
* **What this map is not.** It says which document a note becomes, never what
|
|
46
46
|
* that document contains: the `system` block comes from the item registry's
|
|
@@ -59,7 +59,7 @@ import { defineDocumentSubtypes } from "../engine/document-subtypes.mjs";
|
|
|
59
59
|
*
|
|
60
60
|
* The Item rows are the thirteen types the item registry declares; the one
|
|
61
61
|
* Actor row is `being`, which was two types (`character` and `creature`)
|
|
62
|
-
* compiling to the same actor
|
|
62
|
+
* compiling to the same actor before they were retired. Types this
|
|
63
63
|
* map does not name — `doc`, `macro`, the three map types — compile into
|
|
64
64
|
* documents that carry no system subtype at all, so they have no row and never
|
|
65
65
|
* needed one.
|
package/sohl/index.mjs
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Lands data model that a generic content module must never receive.
|
|
17
17
|
*
|
|
18
18
|
* The item-type registry and its builders, the items and actors compilers, the
|
|
19
|
-
* default-art map, and the affiliation standings live here
|
|
19
|
+
* default-art map, and the affiliation standings live here. Nothing in
|
|
20
20
|
* `@heroiclands/package-build/engine` exports any of it, so an adventure module
|
|
21
21
|
* that builds journals, macros, and scenes never receives `buildWeaponGear`.
|
|
22
22
|
*
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
/** The item-type registry: every type that compiles into an Item, and its builder. */
|
|
34
34
|
export * as itemBuilders from "./item-builders.mjs";
|
|
35
35
|
|
|
36
|
-
/** This system's note-type → document-subtype map, identity rows included
|
|
36
|
+
/** This system's note-type → document-subtype map, identity rows included. */
|
|
37
37
|
export * as documentSubtypes from "./document-subtypes.mjs";
|
|
38
38
|
|
|
39
39
|
/** The Item compiler. */
|
|
@@ -47,7 +47,7 @@ export * as kbPasses from "./kb-passes.mjs";
|
|
|
47
47
|
|
|
48
48
|
// Flat as well as namespaced: the Foundry runtime imports these by name through
|
|
49
49
|
// their own entry points, and they were this barrel's surface before the
|
|
50
|
-
// compilers arrived
|
|
50
|
+
// compilers arrived.
|
|
51
51
|
export { DEFAULT_ITEM_ART, defaultItemArt } from "./default-item-art.mjs";
|
|
52
52
|
export { AFFILIATION_STANDINGS } from "./affiliation-standings.mjs";
|
|
53
53
|
export { BEING_TYPE, GEAR_TYPE_TO_KEY, deriveBeingInfo, isBeing } from "./being-info.mjs";
|
package/sohl/item-builders.mjs
CHANGED
|
@@ -18,20 +18,20 @@
|
|
|
18
18
|
* There is one list, not two. This repository hands {@link ITEM_BUILDERS} to
|
|
19
19
|
* the build as `itemBuilders` in `package-build.config.yaml`, and `itemTypes()`
|
|
20
20
|
* — the whitelist — is derived from that table's own keys, so a type cannot be
|
|
21
|
-
* whitelisted for compilation without a builder to compile it.
|
|
21
|
+
* whitelisted for compilation without a builder to compile it. Split, the
|
|
22
22
|
* whitelist and the builder table were maintained by hand and had already
|
|
23
|
-
* drifted: `trait` —
|
|
23
|
+
* drifted: `trait` — a **retired** item type, absent from
|
|
24
24
|
* `documentTypes.Item` and reported by world migration as unrecognized — was
|
|
25
25
|
* still advertised as compilable, so a `type: trait` note passed the gate and
|
|
26
26
|
* then died on `BUILDERS[type] is not a function`, swallowed as a per-file
|
|
27
|
-
* error
|
|
27
|
+
* error.
|
|
28
28
|
*
|
|
29
29
|
* **The builders are generated, not written.** Each type's `system` builder
|
|
30
30
|
* comes from its field declaration in `item-fields.mjs` by way of
|
|
31
31
|
* {@link buildFromFields}, so the vocabulary a note may write is readable data
|
|
32
32
|
* rather than statements buried in a function body — which is what lets the
|
|
33
|
-
* authoring reference be generated and a note be linted against its type
|
|
34
|
-
*
|
|
33
|
+
* authoring reference be generated and a note be linted against its type.
|
|
34
|
+
* One consequence worth naming: the registry's keys are now
|
|
35
35
|
* {@link ITEM_FIELDS}'s keys, so adding an item type is one edit *there* (plus
|
|
36
36
|
* its `documentTypes.Item` declaration and its default art), and removing one
|
|
37
37
|
* is likewise a single deletion.
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* own evaluation. The table travels into configuration; the engine's
|
|
44
44
|
* `item-registry.mjs` reads it back out and the Item compiler dispatches
|
|
45
45
|
* through that, which is how a consumer's own table is the one its notes
|
|
46
|
-
* compile with
|
|
46
|
+
* compile with.
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
import { defaultItemArt } from "./default-item-art.mjs";
|
|
@@ -86,7 +86,7 @@ const FINALIZERS = Object.freeze({
|
|
|
86
86
|
*
|
|
87
87
|
* **The art map is keyed by the document subtype**, which the runtime reads
|
|
88
88
|
* with a Foundry `Item`'s own `type`, so this asks SoHL's map what an `armor`
|
|
89
|
-
* note becomes before looking art up
|
|
89
|
+
* note becomes before looking art up. Translating on the build side is
|
|
90
90
|
* the only place it can happen: the runtime has no note in hand. Every SoHL
|
|
91
91
|
* Item row is one-to-one, so `documentSubtype` needs no frontmatter and cannot
|
|
92
92
|
* throw for a discriminator.
|
package/sohl/item-fields.mjs
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* This is the authority for what an item note may write, and — because
|
|
18
18
|
* {@link buildFromFields} turns each list into the builder that runs — it is
|
|
19
19
|
* the same authority the compiler obeys. A field that is not here is not
|
|
20
|
-
* emitted; a description here is a description of the thing that executes
|
|
21
|
-
*
|
|
20
|
+
* emitted; a description here is a description of the thing that executes.
|
|
21
|
+
* The per-type authoring reference on the knowledgebase is generated
|
|
22
22
|
* from this file.
|
|
23
23
|
*
|
|
24
24
|
* **SoHL type vocabulary, so it lives in `sohl/`.** `engine/` holds the
|
|
@@ -197,7 +197,7 @@ const STRIKE_MODES = Object.freeze({
|
|
|
197
197
|
* depends on it.
|
|
198
198
|
*
|
|
199
199
|
* Authored `impact.die`, stored `impactBase.die` — the one re-reading field
|
|
200
|
-
* whose two positions are spelled differently, so both are named
|
|
200
|
+
* whose two positions are spelled differently, so both are named.
|
|
201
201
|
*
|
|
202
202
|
* @param {object} fm - The note's frontmatter.
|
|
203
203
|
* @returns {number} The die size, `0` when the projectile declares none.
|
|
@@ -340,8 +340,7 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
340
340
|
},
|
|
341
341
|
{
|
|
342
342
|
// Plural because the field holds a map of many standings, one per
|
|
343
|
-
// affiliation — the singular
|
|
344
|
-
// (SoHL#1781).
|
|
343
|
+
// affiliation — the singular reads as a misnomer.
|
|
345
344
|
name: "relations",
|
|
346
345
|
to: "relations",
|
|
347
346
|
...RELATION,
|
|
@@ -448,7 +447,7 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
448
447
|
describe:
|
|
449
448
|
"What running the course to the end does to the host: `death`, or the benign default `cured`.",
|
|
450
449
|
},
|
|
451
|
-
// The two **authored** thirds of each timed-phase triplet
|
|
450
|
+
// The two **authored** thirds of each timed-phase triplet. Every
|
|
452
451
|
// one omits its key when the note does not carry it, because the value
|
|
453
452
|
// that stands otherwise is the DataModel's `initial: null` — writing a
|
|
454
453
|
// compile-time `null` over it would say "this phase takes no time",
|
|
@@ -513,7 +512,7 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
513
512
|
"The resolution interval in seconds, standing in for a roll of the formula. Omitted when unset.",
|
|
514
513
|
},
|
|
515
514
|
// The `…Date` third of every timed-phase triplet, declared as **runtime
|
|
516
|
-
// state
|
|
515
|
+
// state**. A phase is authored as its `…DurationFormula`; the
|
|
517
516
|
// `…DurationBase` records what that rolled to; the date records *when
|
|
518
517
|
// it happened*, and only play can know that. All four are
|
|
519
518
|
// `worldTimeDateField()`s in SoHL's schema — nullable rather than
|
|
@@ -949,7 +948,7 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
949
948
|
default: null,
|
|
950
949
|
describe: "Shortcode of the body location injured. Unset on a descriptive condition.",
|
|
951
950
|
},
|
|
952
|
-
// A trauma's three timed phases
|
|
951
|
+
// A trauma's three timed phases. Its healing check and course
|
|
953
952
|
// both fall back to a **world setting** when the trauma sets neither
|
|
954
953
|
// half, which is the sharper reason these omit rather than default: a
|
|
955
954
|
// compile-time `null` written here is still a value, and the seeding in
|
|
@@ -1007,7 +1006,7 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
1007
1006
|
describe:
|
|
1008
1007
|
"The course-test interval in seconds, standing in for a roll of the formula. Omitted when unset.",
|
|
1009
1008
|
},
|
|
1010
|
-
// Runtime state, for the reason `affliction`'s four are
|
|
1009
|
+
// Runtime state, for the reason `affliction`'s four are: a
|
|
1011
1010
|
// trauma's dates are crystallized when the phase fires, and world time
|
|
1012
1011
|
// does not exist while content is compiled.
|
|
1013
1012
|
{
|