@heroiclands/package-build 16.0.0 → 17.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/bin/package-build.mjs +73 -0
- package/docs/content-format.md +7 -1
- package/engine/note-schemas.mjs +43 -0
- package/engine/note-vocabulary.mjs +119 -2
- package/package.json +1 -1
- package/sohl/note-schemas.mjs +17 -0
- package/types/engine/note-vocabulary.d.mts +50 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 17.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9d6a52f: **New: `package-build labels check`.** The issue-label registry has two faces
|
|
8
|
+
that must agree — `.github/labels.yml`, which is synced to GitHub, and the §3
|
|
9
|
+
table in `.github/ISSUE_REPORTING.md`, which is what a person reads. Neither
|
|
10
|
+
derives from the other, so either can drift, and nothing notices until an issue
|
|
11
|
+
is filed against a label that does not exist or the sync pushes one the
|
|
12
|
+
documentation never mentions.
|
|
13
|
+
|
|
14
|
+
It arrives here because **every repository wants it and only the paths ever
|
|
15
|
+
differed** — it was a `utils/check-labels.mjs` copied per repository, which is
|
|
16
|
+
the shape a shared check takes just before its copies start disagreeing. Same
|
|
17
|
+
argument that moved the no-attribution check to a shared action.
|
|
18
|
+
|
|
19
|
+
Two things improve in the move:
|
|
20
|
+
|
|
21
|
+
- **Findings are compiler-parseable and located.** The original printed prose
|
|
22
|
+
to stderr; this reports `.github/labels.yml:73:9: error: …` against the file
|
|
23
|
+
each finding belongs to, so a missing row is located in the documentation and
|
|
24
|
+
a missing entry in the registry.
|
|
25
|
+
- **The documentation path is an option.** `--doc` defaults to
|
|
26
|
+
`.github/ISSUE_REPORTING.md`; `Song-of-Heroic-Lands-FoundryVTT` keeps its at
|
|
27
|
+
`kb/dev-docs/how-to/issue-reporting.md` and now needs no separate script.
|
|
28
|
+
`--registry` likewise.
|
|
29
|
+
|
|
30
|
+
A missing §3 is reported once, as its own failure, rather than as every label
|
|
31
|
+
having drifted. An over-long description is caught here too — GitHub answers a
|
|
32
|
+
bare 422 naming neither the label nor the limit.
|
|
33
|
+
|
|
34
|
+
Verified against every repository that has a registry: `sohl-thalorna` (12),
|
|
35
|
+
`sohl-kethira-basic` (11), `harn-adventures` (11), `harn-ensemble` (12) and
|
|
36
|
+
`Song-of-Heroic-Lands-FoundryVTT` (16) all agree — and it found real drift in
|
|
37
|
+
`HarnMaster-3-FoundryVTT`, where `good first issue` and `help wanted` are in
|
|
38
|
+
the registry and absent from §3.
|
|
39
|
+
|
|
40
|
+
## 17.0.0
|
|
41
|
+
|
|
42
|
+
### Major Changes
|
|
43
|
+
|
|
44
|
+
- fdd7094: **The linter now implements the format it publishes.** `docs/content-format.md`
|
|
45
|
+
and the vocabulary had drifted, silently and in both directions, and nothing
|
|
46
|
+
compared them.
|
|
47
|
+
|
|
48
|
+
**Five documented types reached no schema** — `place`, `lore`, `scenario`,
|
|
49
|
+
`vehicle` and `armorlocation`. A note using one was reported as having no
|
|
50
|
+
schema and then _skipped entirely_: `lintNote` returns after that finding, so
|
|
51
|
+
the note's `data:`, `subType`, references and system block all went
|
|
52
|
+
unexamined. So the types were legal enough to author and not legal enough to
|
|
53
|
+
check, and using one **suppressed** every other check on the note (#231).
|
|
54
|
+
|
|
55
|
+
**Three documented `data` properties reached no vocabulary** — `epithet` and
|
|
56
|
+
`symbol` on `affiliation`, and `lore` on both `affiliation` and `being`. The
|
|
57
|
+
`data:` container is closed, so a note that followed the specification exactly
|
|
58
|
+
was told its property did not exist _and the value was dropped_ rather than
|
|
59
|
+
reaching the page (#232).
|
|
60
|
+
|
|
61
|
+
`peoples` is **removed**, having widened to `lore` — the specification says so
|
|
62
|
+
outright, and no tree authors it: "Nothing is lost by widening: the target's
|
|
63
|
+
own subType already distinguishes a `folk` from a `law`." That removal is the
|
|
64
|
+
reason this is a major; everything else here turns red trees green.
|
|
65
|
+
|
|
66
|
+
Between them, on `sohl-thalorna`: **2,001 findings become 20**. The 1,981 that
|
|
67
|
+
go were never content defects. The 20 that stay are: 18 embedded shortcode
|
|
68
|
+
collisions and two genuinely stale keys.
|
|
69
|
+
|
|
70
|
+
**The specification is executable now.** `tests/content-format-agreement.test.ts`
|
|
71
|
+
parses `docs/content-format.md` and fails if a documented type has no schema or
|
|
72
|
+
no vocabulary, or if a type's declared `data` properties differ in either
|
|
73
|
+
direction from its documented table. Nothing compared the two before, which is
|
|
74
|
+
why this drift lasted.
|
|
75
|
+
|
|
76
|
+
Also corrects the specification itself: `macro`'s `macroType` and `macroScope`
|
|
77
|
+
were tabled as `data` properties, but the compiler reads them with the
|
|
78
|
+
`sohl`-field accessor — the `sohl:` block, then the note's top level — so
|
|
79
|
+
`data.macroType` is not read at all. They are `sohl` properties, and the table
|
|
80
|
+
now says so.
|
|
81
|
+
|
|
3
82
|
## 16.0.0
|
|
4
83
|
|
|
5
84
|
### Major Changes
|
package/bin/package-build.mjs
CHANGED
|
@@ -83,6 +83,7 @@ import { cleanBuildArtifacts, stageAssets } from "../stage.mjs";
|
|
|
83
83
|
import { buildSchemaArtifact } from "../engine/schema-extract.mjs";
|
|
84
84
|
import { SCHEMA_ARTIFACT_FILE } from "../engine/foreign-catalog.mjs";
|
|
85
85
|
import { validateLangSource } from "../lang.mjs";
|
|
86
|
+
import { checkLabelRegistry } from "../labels.mjs";
|
|
86
87
|
import {
|
|
87
88
|
analyzeCoverage,
|
|
88
89
|
collectScriptReferences,
|
|
@@ -712,6 +713,77 @@ function langHardcoded(config) {
|
|
|
712
713
|
*
|
|
713
714
|
* @returns {object} The yargs command module.
|
|
714
715
|
*/
|
|
716
|
+
/**
|
|
717
|
+
* `labels check` — do the machine registry and the documented table agree?
|
|
718
|
+
*
|
|
719
|
+
* `.github/labels.yml` is synced to GitHub and the §3 table in
|
|
720
|
+
* `.github/ISSUE_REPORTING.md` is what a person reads. Neither derives from the
|
|
721
|
+
* other, so either can drift, and nothing notices until an issue is filed
|
|
722
|
+
* against a label that does not exist.
|
|
723
|
+
*
|
|
724
|
+
* Every repository wants this and only the paths ever differed, which is why it
|
|
725
|
+
* is here rather than copied into each as a `utils/` script.
|
|
726
|
+
*
|
|
727
|
+
* @returns {object} The yargs command module.
|
|
728
|
+
*/
|
|
729
|
+
function labelsCommand() {
|
|
730
|
+
return {
|
|
731
|
+
command: "labels <action>",
|
|
732
|
+
describe: "Issue-label registry checks",
|
|
733
|
+
builder: (y) =>
|
|
734
|
+
y
|
|
735
|
+
.positional("action", {
|
|
736
|
+
choices: ["check"],
|
|
737
|
+
describe: "check: the registry and the documented table name the same labels",
|
|
738
|
+
})
|
|
739
|
+
.option("registry", {
|
|
740
|
+
type: "string",
|
|
741
|
+
default: ".github/labels.yml",
|
|
742
|
+
describe: "The machine registry synced to GitHub",
|
|
743
|
+
})
|
|
744
|
+
.option("doc", {
|
|
745
|
+
type: "string",
|
|
746
|
+
default: ".github/ISSUE_REPORTING.md",
|
|
747
|
+
describe: "The documented reference whose §3 table lists the same labels",
|
|
748
|
+
}),
|
|
749
|
+
handler: handler(async (args) => labelsCheck(args)),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Read both faces of the registry and report where they disagree.
|
|
755
|
+
*
|
|
756
|
+
* Findings are reported against the file each belongs to, so a missing row is
|
|
757
|
+
* located in the documentation and a missing entry in the registry, rather than
|
|
758
|
+
* both being attributed to whichever file was read first.
|
|
759
|
+
*
|
|
760
|
+
* @param {object} args - Parsed CLI arguments.
|
|
761
|
+
*/
|
|
762
|
+
function labelsCheck(args) {
|
|
763
|
+
for (const target of [args.registry, args.doc]) {
|
|
764
|
+
if (!fs.existsSync(target)) die(`labels check: ${target} does not exist.`);
|
|
765
|
+
}
|
|
766
|
+
const registryText = fs.readFileSync(args.registry, "utf8");
|
|
767
|
+
const docText = fs.readFileSync(args.doc, "utf8");
|
|
768
|
+
|
|
769
|
+
const { registry, doc, count } = checkLabelRegistry({
|
|
770
|
+
registryText,
|
|
771
|
+
docText,
|
|
772
|
+
docPath: args.doc,
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
const errors =
|
|
776
|
+
reportFindings(registry, { file: args.registry }) + reportFindings(doc, { file: args.doc });
|
|
777
|
+
if (errors) {
|
|
778
|
+
console.error(
|
|
779
|
+
`\nThe registry and ${args.doc} §3 are edited together — ` +
|
|
780
|
+
`change one and change the other.`,
|
|
781
|
+
);
|
|
782
|
+
process.exit(1);
|
|
783
|
+
}
|
|
784
|
+
console.log(`package-build: registry and §3 agree (${count} labels).`);
|
|
785
|
+
}
|
|
786
|
+
|
|
715
787
|
function langCommand() {
|
|
716
788
|
return {
|
|
717
789
|
command: "lang <action>",
|
|
@@ -992,6 +1064,7 @@ yargs(hideBin(process.argv))
|
|
|
992
1064
|
.command(manifestCommand())
|
|
993
1065
|
.command(schemaCommand())
|
|
994
1066
|
.command(langCommand())
|
|
1067
|
+
.command(labelsCommand())
|
|
995
1068
|
.command(bundleCommand())
|
|
996
1069
|
.command(releaseCommand())
|
|
997
1070
|
.command(deployCommand())
|
package/docs/content-format.md
CHANGED
|
@@ -1463,7 +1463,13 @@ shipped as content and run by Foundry's own macro runner under the permission
|
|
|
1463
1463
|
model that governs every macro in a world. Nothing evaluates, compiles, or
|
|
1464
1464
|
revives anything; the compiler copies text from a fence into a JSON field.
|
|
1465
1465
|
|
|
1466
|
-
|
|
1466
|
+
**Both settings are `sohl` properties, not `data` ones.** The compiler reads
|
|
1467
|
+
them with the same accessor every `sohl` field uses — the `sohl:` block first,
|
|
1468
|
+
then the note's top level — so `data.macroType` is not read, and a macro is not
|
|
1469
|
+
a journal-only note the way `place`, `lore` and `scenario` are: it produces a
|
|
1470
|
+
Foundry **Macro**, and these two describe that document.
|
|
1471
|
+
|
|
1472
|
+
| `sohl` property | Values | Description |
|
|
1467
1473
|
| --------------- | --------------------------- | -------------------------------------------------------------------- |
|
|
1468
1474
|
| `macroType` | `script` | The Foundry macro type. Defaults to `script`, and `chat` is an error |
|
|
1469
1475
|
| `macroScope` | `global \| actors \| actor` | How far the macro reaches. Defaults to `global` |
|
package/engine/note-schemas.mjs
CHANGED
|
@@ -34,6 +34,45 @@
|
|
|
34
34
|
|
|
35
35
|
import { HOMEPAGE_FIELDS, HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Every engine-level content type, and what a note of that type may write.
|
|
39
|
+
*
|
|
40
|
+
* @type {Readonly<Record<string, readonly import("./field-spec.mjs").FieldSpec[]>>}
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* A note that compiles to a JournalEntry and nothing else.
|
|
44
|
+
*
|
|
45
|
+
* Empty on purpose, and the emptiness is the declaration. `place`, `lore` and
|
|
46
|
+
* `scenario` produce prose — the JournalEntry every note produces — so none of
|
|
47
|
+
* them writes a `sohl:` field, and the authored corpus agrees: across
|
|
48
|
+
* `sohl-thalorna`'s 249 places, 180 lore notes and 21 scenarios, not one
|
|
49
|
+
* carries a `sohl:` block. Everything they *do* declare is `data:`, which is
|
|
50
|
+
* the closed container `engine/note-vocabulary.mjs` holds them to.
|
|
51
|
+
*
|
|
52
|
+
* Declaring the type with no fields is what distinguishes **a type with no
|
|
53
|
+
* vocabulary** from **a type that is unknown** — two different findings, and
|
|
54
|
+
* only the second is a mistake. Until this landed the specification declared
|
|
55
|
+
* all three and nothing implemented them, so a note using one was reported as
|
|
56
|
+
* having no schema and then *skipped entirely*: `lintNote` returns after that
|
|
57
|
+
* finding, so the note's `data:`, `subType`, references and system block all
|
|
58
|
+
* went unexamined (#231).
|
|
59
|
+
*
|
|
60
|
+
* @type {readonly import("./field-spec.mjs").FieldSpec[]}
|
|
61
|
+
*/
|
|
62
|
+
const JOURNAL_ONLY_FIELDS = Object.freeze([]);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* An `armorlocation` note — an HM3 item, and no part of the SoHL registry.
|
|
66
|
+
*
|
|
67
|
+
* It lives here rather than in `sohl/` for the reachability reason that
|
|
68
|
+
* governs this whole file: a package declaring no `itemBuilders` never loads
|
|
69
|
+
* the SoHL registry, and HM3 packages are exactly the ones that author this
|
|
70
|
+
* type.
|
|
71
|
+
*
|
|
72
|
+
* @type {readonly import("./field-spec.mjs").FieldSpec[]}
|
|
73
|
+
*/
|
|
74
|
+
const ARMORLOCATION_FIELDS = Object.freeze([]);
|
|
75
|
+
|
|
37
76
|
/**
|
|
38
77
|
* Every engine-level content type, and what a note of that type may write.
|
|
39
78
|
*
|
|
@@ -41,4 +80,8 @@ import { HOMEPAGE_FIELDS, HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
|
41
80
|
*/
|
|
42
81
|
export const ENGINE_NOTE_SCHEMAS = Object.freeze({
|
|
43
82
|
[HOMEPAGE_TYPE]: HOMEPAGE_FIELDS,
|
|
83
|
+
place: JOURNAL_ONLY_FIELDS,
|
|
84
|
+
lore: JOURNAL_ONLY_FIELDS,
|
|
85
|
+
scenario: JOURNAL_ONLY_FIELDS,
|
|
86
|
+
armorlocation: ARMORLOCATION_FIELDS,
|
|
44
87
|
});
|
|
@@ -395,7 +395,13 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
395
395
|
{ name: "archetypes", ...LIST, describe: "Archetypal behaviours the being fits." },
|
|
396
396
|
{ name: "occupation", ...TEXT, describe: "What the being does for a living." },
|
|
397
397
|
{ name: "stations", ...LINKS, describe: "Stations the being holds." },
|
|
398
|
-
{
|
|
398
|
+
{
|
|
399
|
+
name: "lore",
|
|
400
|
+
...LINKS,
|
|
401
|
+
describe:
|
|
402
|
+
"Lore concerning this being — the people it is of, the standing it " +
|
|
403
|
+
"holds, the law it lives under.",
|
|
404
|
+
},
|
|
399
405
|
{ name: "homes", ...LINKS, describe: "Places the being calls home." },
|
|
400
406
|
{
|
|
401
407
|
name: "affiliations",
|
|
@@ -425,6 +431,14 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
425
431
|
]),
|
|
426
432
|
}),
|
|
427
433
|
|
|
434
|
+
vehicle: Object.freeze({
|
|
435
|
+
subTypes: null,
|
|
436
|
+
data: Object.freeze([
|
|
437
|
+
{ name: "portrait", ...TEXT, describe: "Path to the portrait image." },
|
|
438
|
+
TEMPLATE_PRIORITY,
|
|
439
|
+
]),
|
|
440
|
+
}),
|
|
441
|
+
|
|
428
442
|
/* ----- items ---------------------------------------------------- */
|
|
429
443
|
|
|
430
444
|
affiliation: Object.freeze({
|
|
@@ -448,6 +462,18 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
448
462
|
...TEXT,
|
|
449
463
|
describe: "What one member is called — a Vylarian.",
|
|
450
464
|
},
|
|
465
|
+
{
|
|
466
|
+
name: "epithet",
|
|
467
|
+
...TEXT,
|
|
468
|
+
describe: "The by-name it is known by — a god's, an order's, a company's.",
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
name: "symbol",
|
|
472
|
+
...TEXT,
|
|
473
|
+
describe:
|
|
474
|
+
"Its emblem in words: a feather atop a golden scale, a chisel carving " +
|
|
475
|
+
"a star.",
|
|
476
|
+
},
|
|
451
477
|
{
|
|
452
478
|
name: "governance.model",
|
|
453
479
|
...TEXT,
|
|
@@ -481,7 +507,13 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
481
507
|
...LINKS,
|
|
482
508
|
describe: "What its economic life runs on — currencies, banking bodies, goods.",
|
|
483
509
|
},
|
|
484
|
-
{
|
|
510
|
+
{
|
|
511
|
+
name: "lore",
|
|
512
|
+
...LINKS,
|
|
513
|
+
describe:
|
|
514
|
+
"Lore concerning it — the peoples it draws on, the god a faith " +
|
|
515
|
+
"venerates, its law, its calendar.",
|
|
516
|
+
},
|
|
485
517
|
{ name: "parents", ...LINKS, describe: "Affiliations it is subordinate to." },
|
|
486
518
|
{
|
|
487
519
|
name: "relations",
|
|
@@ -532,6 +564,11 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
532
564
|
data: Object.freeze([TEMPLATE_PRIORITY, ...GEAR]),
|
|
533
565
|
}),
|
|
534
566
|
|
|
567
|
+
armorlocation: Object.freeze({
|
|
568
|
+
subTypes: null,
|
|
569
|
+
data: Object.freeze([TEMPLATE_PRIORITY]),
|
|
570
|
+
}),
|
|
571
|
+
|
|
535
572
|
attribute: Object.freeze({
|
|
536
573
|
data: Object.freeze([TEMPLATE_PRIORITY]),
|
|
537
574
|
}),
|
|
@@ -674,6 +711,86 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
674
711
|
|
|
675
712
|
macro: Object.freeze({ data: Object.freeze([]) }),
|
|
676
713
|
|
|
714
|
+
lore: Object.freeze({
|
|
715
|
+
subTypes: Object.freeze([
|
|
716
|
+
"cosmology",
|
|
717
|
+
"deity",
|
|
718
|
+
"theology",
|
|
719
|
+
"arcana",
|
|
720
|
+
"spirit",
|
|
721
|
+
"economy",
|
|
722
|
+
"law",
|
|
723
|
+
"calendar",
|
|
724
|
+
"history",
|
|
725
|
+
"material",
|
|
726
|
+
"folk",
|
|
727
|
+
"culture",
|
|
728
|
+
"bestiary",
|
|
729
|
+
]),
|
|
730
|
+
// Nothing of its own: a lore note is prose, and what it *is* about is
|
|
731
|
+
// its subType. The specification declares an empty table for it, and
|
|
732
|
+
// the authored corpus writes no `data:` key on any of the 180.
|
|
733
|
+
data: Object.freeze([]),
|
|
734
|
+
}),
|
|
735
|
+
|
|
736
|
+
place: Object.freeze({
|
|
737
|
+
subTypes: Object.freeze(["world", "region", "settlement", "site", "structure", "feature"]),
|
|
738
|
+
data: Object.freeze([
|
|
739
|
+
{
|
|
740
|
+
name: "demonym",
|
|
741
|
+
...TEXT,
|
|
742
|
+
describe: "What a person from this place is called — a Vylarian.",
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
name: "lore",
|
|
746
|
+
...LINKS,
|
|
747
|
+
describe:
|
|
748
|
+
"Lore concerning this place — its peoples, its law, its calendar, " +
|
|
749
|
+
"its history.",
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
name: "parents",
|
|
753
|
+
...LINKS,
|
|
754
|
+
describe: "Enclosing places this one sits within.",
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
name: "population",
|
|
758
|
+
...NUM,
|
|
759
|
+
describe: "Approximate population, to two significant digits.",
|
|
760
|
+
},
|
|
761
|
+
]),
|
|
762
|
+
}),
|
|
763
|
+
|
|
764
|
+
scenario: Object.freeze({
|
|
765
|
+
subTypes: Object.freeze(["campaign", "adventure", "encounter"]),
|
|
766
|
+
data: Object.freeze([
|
|
767
|
+
{ name: "parents", ...LINKS, describe: "Scenarios this one sits within." },
|
|
768
|
+
{ name: "locations", ...LINKS, describe: "Places the scenario takes place in." },
|
|
769
|
+
{ name: "cast", ...LINKS, describe: "Beings who appear in it." },
|
|
770
|
+
{ name: "factions", ...LINKS, describe: "Affiliations with a stake in it." },
|
|
771
|
+
{
|
|
772
|
+
name: "follows",
|
|
773
|
+
...LINKS,
|
|
774
|
+
describe: "Scenarios that should be played before this one.",
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: "status",
|
|
778
|
+
...TEXT,
|
|
779
|
+
describe: "`draft`, `playtested` or `published`.",
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
name: "party.size",
|
|
783
|
+
...TEXT,
|
|
784
|
+
describe: "`solo`, `small`, `standard`, `large` or `host`.",
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
name: "party.archetypes",
|
|
788
|
+
...LIST,
|
|
789
|
+
describe: "Archetypes the scenario is written for.",
|
|
790
|
+
},
|
|
791
|
+
]),
|
|
792
|
+
}),
|
|
793
|
+
|
|
677
794
|
homepage: Object.freeze({ data: Object.freeze([]) }),
|
|
678
795
|
|
|
679
796
|
map: Object.freeze({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.1.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",
|
package/sohl/note-schemas.mjs
CHANGED
|
@@ -87,6 +87,22 @@ const MACRO_FIELDS = Object.freeze([
|
|
|
87
87
|
*
|
|
88
88
|
* @type {readonly import("../engine/field-spec.mjs").FieldSpec[]}
|
|
89
89
|
*/
|
|
90
|
+
/**
|
|
91
|
+
* A `vehicle` note — a conveyance that carries goods and people.
|
|
92
|
+
*
|
|
93
|
+
* Empty, and the emptiness is the declaration: the specification gives a
|
|
94
|
+
* vehicle two `data` properties and no `sohl:` field of its own, and the
|
|
95
|
+
* closed `data:` container is where `engine/note-vocabulary.mjs` holds them.
|
|
96
|
+
* Declaring the type with no fields distinguishes a type with no vocabulary
|
|
97
|
+
* from a type that is unknown, which are different findings (#231).
|
|
98
|
+
*
|
|
99
|
+
* It is here rather than in the engine because a vehicle is a SoHL actor —
|
|
100
|
+
* the specification maps it to `sohl` and marks it NA for hm3.
|
|
101
|
+
*
|
|
102
|
+
* @type {readonly import("../engine/field-spec.mjs").FieldSpec[]}
|
|
103
|
+
*/
|
|
104
|
+
const VEHICLE_FIELDS = Object.freeze([]);
|
|
105
|
+
|
|
90
106
|
const BEING_FIELDS = Object.freeze([
|
|
91
107
|
{
|
|
92
108
|
name: "body",
|
|
@@ -330,5 +346,6 @@ export const NOTE_SCHEMAS = Object.freeze({
|
|
|
330
346
|
doc: DOC_FIELDS,
|
|
331
347
|
macro: MACRO_FIELDS,
|
|
332
348
|
being: Object.freeze([...BEING_FIELDS, ...PRESENTATION_FIELDS.being]),
|
|
349
|
+
vehicle: VEHICLE_FIELDS,
|
|
333
350
|
map: MAP_FIELDS,
|
|
334
351
|
});
|
|
@@ -187,6 +187,10 @@ export const NOTE_VOCABULARY: Readonly<{
|
|
|
187
187
|
subTypes: null;
|
|
188
188
|
data: readonly DataFieldSpec[];
|
|
189
189
|
}>;
|
|
190
|
+
vehicle: Readonly<{
|
|
191
|
+
subTypes: null;
|
|
192
|
+
data: readonly DataFieldSpec[];
|
|
193
|
+
}>;
|
|
190
194
|
affiliation: Readonly<{
|
|
191
195
|
subTypes: readonly string[];
|
|
192
196
|
data: readonly DataFieldSpec[];
|
|
@@ -198,6 +202,10 @@ export const NOTE_VOCABULARY: Readonly<{
|
|
|
198
202
|
armorgear: Readonly<{
|
|
199
203
|
data: readonly DataFieldSpec[];
|
|
200
204
|
}>;
|
|
205
|
+
armorlocation: Readonly<{
|
|
206
|
+
subTypes: null;
|
|
207
|
+
data: readonly DataFieldSpec[];
|
|
208
|
+
}>;
|
|
201
209
|
attribute: Readonly<{
|
|
202
210
|
data: readonly DataFieldSpec[];
|
|
203
211
|
}>;
|
|
@@ -256,6 +264,48 @@ export const NOTE_VOCABULARY: Readonly<{
|
|
|
256
264
|
macro: Readonly<{
|
|
257
265
|
data: readonly never[];
|
|
258
266
|
}>;
|
|
267
|
+
lore: Readonly<{
|
|
268
|
+
subTypes: readonly string[];
|
|
269
|
+
data: readonly never[];
|
|
270
|
+
}>;
|
|
271
|
+
place: Readonly<{
|
|
272
|
+
subTypes: readonly string[];
|
|
273
|
+
data: readonly ({
|
|
274
|
+
describe: string;
|
|
275
|
+
shape: "string";
|
|
276
|
+
kind: "string";
|
|
277
|
+
name: string;
|
|
278
|
+
} | {
|
|
279
|
+
describe: string;
|
|
280
|
+
shape: "list of wikilinks";
|
|
281
|
+
kind: "list";
|
|
282
|
+
name: string;
|
|
283
|
+
} | {
|
|
284
|
+
describe: string;
|
|
285
|
+
shape: "number";
|
|
286
|
+
kind: "number";
|
|
287
|
+
name: string;
|
|
288
|
+
})[];
|
|
289
|
+
}>;
|
|
290
|
+
scenario: Readonly<{
|
|
291
|
+
subTypes: readonly string[];
|
|
292
|
+
data: readonly ({
|
|
293
|
+
describe: string;
|
|
294
|
+
shape: "list of wikilinks";
|
|
295
|
+
kind: "list";
|
|
296
|
+
name: string;
|
|
297
|
+
} | {
|
|
298
|
+
describe: string;
|
|
299
|
+
shape: "string";
|
|
300
|
+
kind: "string";
|
|
301
|
+
name: string;
|
|
302
|
+
} | {
|
|
303
|
+
describe: string;
|
|
304
|
+
shape: "list";
|
|
305
|
+
kind: "list";
|
|
306
|
+
name: string;
|
|
307
|
+
})[];
|
|
308
|
+
}>;
|
|
259
309
|
homepage: Readonly<{
|
|
260
310
|
data: readonly never[];
|
|
261
311
|
}>;
|