@heroiclands/package-build 7.0.0 → 8.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.
@@ -311,11 +311,6 @@ export const ITEM_FIELDS = Object.freeze({
311
311
  value: false,
312
312
  describe: "Whether it is currently dormant. Play state.",
313
313
  },
314
- {
315
- to: "isTreated",
316
- value: false,
317
- describe: "Whether it has been treated. Play state.",
318
- },
319
314
  {
320
315
  name: "levelBase",
321
316
  to: "levelBase",
@@ -633,22 +628,6 @@ export const ITEM_FIELDS = Object.freeze({
633
628
  required: true,
634
629
  describe: "What kind of projectile it is.",
635
630
  },
636
- {
637
- name: "impact.overrideDice",
638
- to: "impactBase.overrideDice",
639
- shape: "boolean",
640
- read: (raw, { fm }) => Boolean(raw ?? impactDie(fm) > 0),
641
- describe:
642
- "Whether the projectile's dice replace the launching weapon's. Defaults to true when the projectile declares a die.",
643
- },
644
- {
645
- name: "impact.overrideModifier",
646
- to: "impactBase.overrideModifier",
647
- shape: "boolean",
648
- read: (raw) => Boolean(raw ?? false),
649
- describe:
650
- "Whether the projectile's modifier replaces the launching weapon's.",
651
- },
652
631
  {
653
632
  to: "impactBase.numDice",
654
633
  value: (fm) => (impactDie(fm) > 0 ? 1 : 0),
@@ -781,20 +760,6 @@ export const ITEM_FIELDS = Object.freeze({
781
760
  describe:
782
761
  "How the injury was inflicted. Unset on a descriptive condition.",
783
762
  },
784
- {
785
- name: "isTreated",
786
- to: "isTreated",
787
- ...BOOLEAN,
788
- default: false,
789
- describe: "Whether it has been treated.",
790
- },
791
- {
792
- name: "isBleeding",
793
- to: "isBleeding",
794
- ...BOOLEAN,
795
- default: false,
796
- describe: "Whether it is bleeding.",
797
- },
798
763
  {
799
764
  name: "bodyLocationCode",
800
765
  to: "bodyLocationCode",
@@ -23,6 +23,15 @@ export function itemCatalogRelationships(config: object): Array<{
23
23
  * @returns {string} The directory.
24
24
  */
25
25
  export function catalogDir(config: object, id: string, version: string): string;
26
+ /**
27
+ * Where a cached dependency's published schema sits, if it shipped one.
28
+ *
29
+ * @param {object} config - The resolved configuration.
30
+ * @param {string} id - The dependency's package id.
31
+ * @param {string} version - Its resolved version.
32
+ * @returns {string} The path, whether or not it exists.
33
+ */
34
+ export function cachedSchemaPath(config: object, id: string, version: string): string;
26
35
  /**
27
36
  * Write an unzipped archive's entries under `dest`.
28
37
  *
@@ -101,3 +110,9 @@ export function fetchAllCatalogs(config: object): Promise<number>;
101
110
  * @returns {string[]} Every cached dependency's item directories.
102
111
  */
103
112
  export function foreignItemCatalogDirs(config: object): string[];
113
+ /**
114
+ * The file a system publishes its `system` field sets as (#60).
115
+ *
116
+ * @type {string}
117
+ */
118
+ export const SCHEMA_ARTIFACT_FILE: string;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * A system's published field sets.
3
+ *
4
+ * @typedef {object} SchemaArtifact
5
+ * @property {number} version - {@link SCHEMA_ARTIFACT_VERSION}.
6
+ * @property {string} system - The system id the schemas belong to.
7
+ * @property {string} systemVersion - The system version they were read from.
8
+ * @property {Record<string, Record<string, {own: string[], inherited: string[]}>>} documents
9
+ * Document type → subtype → its field paths, dotted for nested schema fields.
10
+ */
11
+ /**
12
+ * Why `own` and `inherited` are recorded apart.
13
+ *
14
+ * A subtype's schema spreads its parent's — `MysticalAbilityDataModel` spreads
15
+ * `SohlItemDataModel`, which spreads the common one — so `notes`, `docHtml` and
16
+ * the rest arrive on every subtype. Those are the system's own runtime
17
+ * concerns, filled by the system rather than by a content builder, and a
18
+ * builder is not expected to emit them.
19
+ *
20
+ * Collapsing the two sets would make the *declared, not emitted* direction
21
+ * report every inherited field on every type: a wall of findings that are all
22
+ * correct and none actionable, which is the shape of report people learn to
23
+ * skip. So the two directions read different sets:
24
+ *
25
+ * | direction | read against | severity |
26
+ * | --- | --- | --- |
27
+ * | emitted, not declared | `own` ∪ `inherited` — the field must exist *somewhere* | error |
28
+ * | declared, not emitted | `own` only — what this subtype adds is what its builder answers for | report |
29
+ *
30
+ * @param {SchemaArtifact} artifact - The published schemas.
31
+ * @param {string} documentType - `Item`, `Actor`, …
32
+ * @param {string} subtype - The document subtype.
33
+ * @returns {{own: Set<string>, all: Set<string>}|null} The sets, or `null` when
34
+ * the artifact declares no such subtype.
35
+ */
36
+ export function declaredFields(artifact: SchemaArtifact, documentType: string, subtype: string): {
37
+ own: Set<string>;
38
+ all: Set<string>;
39
+ } | null;
40
+ /**
41
+ * Every `system` path a field declaration can emit.
42
+ *
43
+ * `buildFromFields` writes each field at its `to`, so the declaration is the
44
+ * emitted key set. A nested `to` (`charges.value`) is recorded whole, and its
45
+ * parents are recorded too: a schema declares `charges` as a `SchemaField` and
46
+ * the path beneath it separately, so a comparison that knew only the leaf would
47
+ * report the container as unemitted and the leaf as undeclared.
48
+ *
49
+ * @param {readonly {to: string}[]} fields - A type's field declaration.
50
+ * @returns {Set<string>} The paths, parents included.
51
+ */
52
+ export function emittedFields(fields: readonly {
53
+ to: string;
54
+ }[]): Set<string>;
55
+ /**
56
+ * Compare one system's builders against one system's published schemas.
57
+ *
58
+ * Pure: field declarations in, findings out. The caller supplies both halves so
59
+ * that a system checking itself and a module checking against a vendored
60
+ * artifact run the identical comparison.
61
+ *
62
+ * @param {object} opts
63
+ * @param {Record<string, readonly {to: string}[]>} opts.builders - Type →
64
+ * field declaration, as `ITEM_FIELDS` holds it.
65
+ * @param {SchemaArtifact} opts.artifact - The receiving system's schemas.
66
+ * @param {string} [opts.documentType="Item"] - Which document type the builders
67
+ * compile into.
68
+ * @param {(type: string) => string} [opts.subtypeOf] - Maps a builder's type to
69
+ * the document subtype it emits. Defaults to identity, which is what the
70
+ * coincidence of names amounts to today (#79) — stated as a seam so that the
71
+ * explicit map replaces a default rather than a hard-coded assumption.
72
+ * @returns {{undeclared: object[], unemitted: object[], skipped: string[]}}
73
+ * `undeclared` fails a build; `unemitted` is reported; `skipped` names the
74
+ * types the artifact says nothing about.
75
+ */
76
+ export function compareFields({ builders, artifact, documentType, subtypeOf, }: {
77
+ builders: Record<string, readonly {
78
+ to: string;
79
+ }[]>;
80
+ artifact: SchemaArtifact;
81
+ documentType?: string | undefined;
82
+ subtypeOf?: ((type: string) => string) | undefined;
83
+ }): {
84
+ undeclared: object[];
85
+ unemitted: object[];
86
+ skipped: string[];
87
+ };
88
+ /**
89
+ * The published schema this build should check itself against, or `null`.
90
+ *
91
+ * **Which system, and which version, are already settled.** `stats.systemId`
92
+ * and `stats.systemVersion` are derived rather than authored (#48) — a system
93
+ * package is its own system, and a module takes the one it requires — and the
94
+ * version is the `compatibility.verified` it pins. So the question "whose
95
+ * schema, at what version" has one answer here rather than a second set of
96
+ * configuration to disagree with the first.
97
+ *
98
+ * Two places to find it, because a system checks itself against source it owns
99
+ * while a module checks against a dependency it fetched:
100
+ *
101
+ * - **A system**: its own `schema.json`, generated from its `src/` and
102
+ * committed beside it.
103
+ * - **A module**: the copy cached by `content-build deps fetch`, from the
104
+ * archive of the version it pins — which is what makes the comparison happen
105
+ * at `verified` rather than against whatever the system's `main` holds today.
106
+ * That distinction is the whole of the `affiliation.subType` case.
107
+ *
108
+ * `null` where there is nothing to check against: a system-agnostic module
109
+ * stamps no system at all, and a system that has not adopted the artifact yet
110
+ * is simply unchecked. Neither is an error, and the caller says which it was.
111
+ *
112
+ * @param {object} config - The resolved build configuration.
113
+ * @returns {{artifact: SchemaArtifact, source: string}|null} The schema and
114
+ * where it was read from.
115
+ */
116
+ export function resolveSchemaArtifact(config: object): {
117
+ artifact: SchemaArtifact;
118
+ source: string;
119
+ } | null;
120
+ /**
121
+ * What an author is told about a field the target system does not define.
122
+ *
123
+ * Names the version, because the same field may be perfectly well defined on
124
+ * the system's `main` and simply unreleased — which is exactly the kethira case,
125
+ * and the difference between "you typed it wrong" and "you are ahead of your
126
+ * pin".
127
+ *
128
+ * @param {object} finding - One entry from `undeclared`.
129
+ * @returns {string} The message.
130
+ */
131
+ export function undeclaredMessage(finding: object): string;
132
+ /**
133
+ * What an author is told about a declared field no builder writes.
134
+ *
135
+ * Advisory rather than fatal: a field the system fills at runtime, or one added
136
+ * ahead of the content that will use it, is not a defect. Only fields the
137
+ * subtype declares *itself* are reported — see {@link declaredFields}.
138
+ *
139
+ * @param {object} finding - One entry from `unemitted`.
140
+ * @returns {string} The message.
141
+ */
142
+ export function unemittedMessage(finding: object): string;
143
+ /**
144
+ * The artifact version this module reads.
145
+ *
146
+ * A mismatch stops the check rather than resolving anyway: a schema read under
147
+ * the wrong shape would report confident nonsense in both directions, and a
148
+ * silently skipped check is the state #60 exists to leave.
149
+ *
150
+ * @type {number}
151
+ */
152
+ export const SCHEMA_ARTIFACT_VERSION: number;
153
+ /**
154
+ * A system's published field sets.
155
+ */
156
+ export type SchemaArtifact = {
157
+ /**
158
+ * - {@link SCHEMA_ARTIFACT_VERSION}.
159
+ */
160
+ version: number;
161
+ /**
162
+ * - The system id the schemas belong to.
163
+ */
164
+ system: string;
165
+ /**
166
+ * - The system version they were read from.
167
+ */
168
+ systemVersion: string;
169
+ /**
170
+ * Document type → subtype → its field paths, dotted for nested schema fields.
171
+ */
172
+ documents: Record<string, Record<string, {
173
+ own: string[];
174
+ inherited: string[];
175
+ }>>;
176
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The `subtype: ClassName` entries of a registry object literal.
3
+ *
4
+ * @param {ts.SourceFile} src - The parsed file holding the registry.
5
+ * @param {string} name - The registry binding, e.g. `itemModels`.
6
+ * @returns {Map<string, string>} Subtype to DataModel class name.
7
+ */
8
+ export function registryOf(src: ts.SourceFile, name: string): Map<string, string>;
9
+ /**
10
+ * The `tsconfig.json` path aliases, read rather than restated.
11
+ *
12
+ * A TypeScript repository may import every DataModel through `@src/…`, so a
13
+ * resolver that understood only relative specifiers would find none of them.
14
+ * Reading the mapping means adding an alias there does not silently make a
15
+ * subtype unreadable here.
16
+ *
17
+ * Absent for a JavaScript repository, which is not an error — it simply has no
18
+ * aliases to resolve.
19
+ *
20
+ * @param {string} rootDir - The repository root.
21
+ * @returns {[string, string][]} Prefix to directory, longest prefix first.
22
+ */
23
+ export function pathAliases(rootDir: string): [string, string][];
24
+ /**
25
+ * Resolve one subtype's schema into own and inherited field paths.
26
+ *
27
+ * @param {object} opts - Resolution inputs.
28
+ * @param {string} opts.file - The file naming the class (registry or importer).
29
+ * @param {string} opts.className - The DataModel class.
30
+ * @param {[string, string][]} opts.aliases - From {@link pathAliases}.
31
+ * @param {Map<string, ts.SourceFile>} opts.cache - The per-run parse memo.
32
+ * @param {string} opts.rootDir - For readable error paths.
33
+ * @returns {{own: string[], inherited: string[]}}
34
+ */
35
+ export function fieldsForClass({ file, className, aliases, cache, rootDir }: {
36
+ file: string;
37
+ className: string;
38
+ aliases: [string, string][];
39
+ cache: Map<string, ts.SourceFile>;
40
+ rootDir: string;
41
+ }): {
42
+ own: string[];
43
+ inherited: string[];
44
+ };
45
+ /**
46
+ * Build the whole artifact from a package's source.
47
+ *
48
+ * @param {object} opts - Inputs.
49
+ * @param {string} opts.rootDir - The repository root.
50
+ * @param {object[]} opts.registries - `{documentType, from, registry}` entries.
51
+ * @param {string} opts.packageId - The Foundry package id.
52
+ * @param {string} opts.version - The package version.
53
+ * @returns {object} The artifact `schema-check.mjs` reads.
54
+ */
55
+ export function buildSchemaArtifact({ rootDir, registries, packageId, version, }: {
56
+ rootDir: string;
57
+ registries: object[];
58
+ packageId: string;
59
+ version: string;
60
+ }): object;
61
+ import ts from "typescript";