@heroiclands/package-build 9.0.0 → 10.0.1

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +721 -0
  2. package/CONTENT.md +273 -13
  3. package/bin/content-build.mjs +437 -7
  4. package/content-config.mjs +259 -28
  5. package/docs/content-format.md +1418 -0
  6. package/engine/address-charset.mjs +62 -0
  7. package/engine/alias-index.mjs +153 -0
  8. package/engine/base-compiler.mjs +194 -4
  9. package/engine/content-address.mjs +4 -4
  10. package/engine/content-format-check.mjs +570 -0
  11. package/engine/content-format.mjs +253 -0
  12. package/engine/content-links.mjs +132 -56
  13. package/engine/content-lint.mjs +8 -1
  14. package/engine/diagnostics.mjs +33 -0
  15. package/engine/document-subtypes.mjs +440 -0
  16. package/engine/field-spec.mjs +49 -43
  17. package/engine/frontmatter-lint.mjs +351 -28
  18. package/engine/generate.mjs +32 -4
  19. package/engine/helpers.mjs +41 -29
  20. package/engine/ids.mjs +19 -1
  21. package/engine/index.mjs +15 -0
  22. package/engine/item-registry.mjs +72 -5
  23. package/engine/kb-manifest.mjs +36 -7
  24. package/engine/map-notes.mjs +34 -18
  25. package/engine/note-claims.mjs +383 -0
  26. package/engine/note-vocabulary.mjs +678 -0
  27. package/engine/pack-config.mjs +39 -22
  28. package/engine/pack-router.mjs +17 -6
  29. package/engine/prose-lint.mjs +55 -3
  30. package/engine/retired-fields.mjs +117 -3
  31. package/engine/scenes.mjs +19 -1
  32. package/engine/schema-check.mjs +347 -3
  33. package/engine/site-build.mjs +1 -1
  34. package/engine/site-index.mjs +38 -21
  35. package/engine/system-block.mjs +513 -0
  36. package/engine/web-wikilinks.mjs +112 -80
  37. package/engine/wikilink-syntax.mjs +30 -0
  38. package/engine/wikilinks.mjs +67 -51
  39. package/package.json +6 -2
  40. package/sohl/actors.mjs +249 -36
  41. package/sohl/document-subtypes.mjs +82 -0
  42. package/sohl/index.mjs +3 -0
  43. package/sohl/items.mjs +110 -14
  44. package/sohl/note-schemas.mjs +11 -7
  45. package/types/content-config.d.mts +48 -4
  46. package/types/engine/address-charset.d.mts +45 -0
  47. package/types/engine/alias-index.d.mts +122 -0
  48. package/types/engine/base-compiler.d.mts +132 -4
  49. package/types/engine/content-address.d.mts +2 -2
  50. package/types/engine/content-format-check.d.mts +163 -0
  51. package/types/engine/content-format.d.mts +101 -0
  52. package/types/engine/content-links.d.mts +16 -1
  53. package/types/engine/content-lint.d.mts +6 -0
  54. package/types/engine/diagnostics.d.mts +29 -0
  55. package/types/engine/document-subtypes.d.mts +233 -0
  56. package/types/engine/field-spec.d.mts +76 -23
  57. package/types/engine/frontmatter-lint.d.mts +47 -2
  58. package/types/engine/generate.d.mts +14 -1
  59. package/types/engine/helpers.d.mts +21 -13
  60. package/types/engine/ids.d.mts +10 -0
  61. package/types/engine/index.d.mts +5 -0
  62. package/types/engine/item-registry.d.mts +21 -2
  63. package/types/engine/kb-manifest.d.mts +35 -8
  64. package/types/engine/map-notes.d.mts +21 -11
  65. package/types/engine/note-claims.d.mts +113 -0
  66. package/types/engine/note-vocabulary.d.mts +251 -0
  67. package/types/engine/pack-config.d.mts +4 -3
  68. package/types/engine/pack-router.d.mts +4 -4
  69. package/types/engine/prose-lint.d.mts +6 -2
  70. package/types/engine/retired-fields.d.mts +73 -2
  71. package/types/engine/schema-check.d.mts +182 -0
  72. package/types/engine/system-block.d.mts +281 -0
  73. package/types/engine/web-wikilinks.d.mts +23 -12
  74. package/types/engine/wikilink-syntax.d.mts +29 -0
  75. package/types/sohl/actors.d.mts +62 -6
  76. package/types/sohl/document-subtypes.d.mts +14 -0
  77. package/types/sohl/index.d.mts +1 -0
  78. package/types/sohl/items.d.mts +21 -0
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Write `value` at a dotted path in a document's `system` block, creating the
3
+ * intermediate objects.
4
+ *
5
+ * Insertion order is the emitted JSON's key order, so a declaration's order is
6
+ * the compiled document's order — which is what lets a field list replace a
7
+ * hand-written object literal without changing a single byte of output.
8
+ *
9
+ * It sits here rather than beside the field declarations because both writers
10
+ * into a `system` block use it: the declared fields, and the verbatim
11
+ * `<system>.system` passthrough. `field-spec.mjs` re-exports it, so the name
12
+ * has one import path as well as one definition.
13
+ *
14
+ * @param {object} target - The object to write into (mutated).
15
+ * @param {string} dotted - Path, e.g. `"locations.flexible"`.
16
+ * @param {any} value - The value to set.
17
+ * @returns {object} `target`, for chaining.
18
+ */
19
+ export function setPath(target: object, dotted: string, value: any): object;
20
+ /**
21
+ * One system's block, or nothing.
22
+ *
23
+ * A block authored as a scalar or a list is **absent** rather than an error
24
+ * here: this module reports what a note carries, and saying what is wrong with
25
+ * a malformed one is the linter's job, which can point at the line.
26
+ *
27
+ * @param {object} fm - The note's frontmatter.
28
+ * @param {string} block - The block key, e.g. `"sohl"`.
29
+ * @returns {Record<string, unknown>|undefined} The block, or `undefined`.
30
+ */
31
+ export function systemBlock(fm: object, block: string): Record<string, unknown> | undefined;
32
+ /**
33
+ * Whether a note carries a system's block at all.
34
+ *
35
+ * This is the pack-eligibility question: a pack declaring a system compiles a
36
+ * note only if the note has something to say about that system. A note that
37
+ * does not carries no system data, and compiling it anyway produces a hollow
38
+ * document — one with a subtype and none of the fields that subtype exists for.
39
+ *
40
+ * @param {object} fm - The note's frontmatter.
41
+ * @param {string} block - The block key.
42
+ * @returns {boolean} True when the block is present and is a mapping.
43
+ */
44
+ export function carriesSystemBlock(fm: object, block: string): boolean;
45
+ /**
46
+ * A system block's `system` sub-block — what maps onto `document.system`.
47
+ *
48
+ * @param {object} fm - The note's frontmatter.
49
+ * @param {string} block - The block key.
50
+ * @returns {Record<string, unknown>} The authored data, `{}` when absent.
51
+ */
52
+ export function systemData(fm: object, block: string): Record<string, unknown>;
53
+ /**
54
+ * Read a key from one system's block, falling back to the top level.
55
+ *
56
+ * The generalization of `sohlField()` to any block, and behaviourally identical
57
+ * to it for `"sohl"` — the one system every existing tree authors. What changed
58
+ * is that the block is a parameter rather than a constant, which is the whole
59
+ * of what a second system needs from this reader.
60
+ *
61
+ * @param {object} fm - The note's frontmatter.
62
+ * @param {string} block - The block key.
63
+ * @param {string} key - The property, dotted for a nested one.
64
+ * @param {any} [defaultValue] - Returned when neither declares it.
65
+ * @returns {any} The value.
66
+ */
67
+ export function blockField(fm: object, block: string, key: string, defaultValue?: any): any;
68
+ /**
69
+ * A **shared** top-level property, read by a possibly-dotted path.
70
+ *
71
+ * Deliberately blind to every system block: this is the third step of the
72
+ * resolution order, and letting a block answer it would make the second step
73
+ * and the third the same question.
74
+ *
75
+ * @param {object} fm - The note's frontmatter.
76
+ * @param {string} source - The property, dotted for a path into a container.
77
+ * @param {any} [defaultValue] - Returned when the path resolves to nothing.
78
+ * @returns {any} The value.
79
+ */
80
+ export function sharedProperty(fm: object, source: string, defaultValue?: any): any;
81
+ /**
82
+ * A property a system block may override, else the shared top-level one.
83
+ *
84
+ * This is what gives `pack`, `effects`, `flags` and `img` their per-system form
85
+ * without inventing a mechanism for each: a note that wants one value for both
86
+ * systems says it once at the top, and a note that needs them to differ says so
87
+ * in the block that differs.
88
+ *
89
+ * @param {object} fm - The note's frontmatter.
90
+ * @param {string} block - The block key.
91
+ * @param {string} key - The property.
92
+ * @param {any} [defaultValue] - Returned when neither declares it.
93
+ * @returns {any} The value.
94
+ */
95
+ export function blockProperty(fm: object, block: string, key: string, defaultValue?: any): any;
96
+ /**
97
+ * Where a declared field's value came from.
98
+ *
99
+ * Reported alongside the value so a caller — a linter, a migration, a test —
100
+ * can distinguish a value an author wrote from one a default supplied, which
101
+ * the value alone never says.
102
+ *
103
+ * @typedef {"system"|"block"|"shared"|"default"|"value"} FieldSource
104
+ */
105
+ /**
106
+ * Resolve one declared field against a note, in the declared order.
107
+ *
108
+ * See the module note for the order and why it is declared rather than
109
+ * name-matched. The value comes back **raw**; applying the field's own `read`
110
+ * is {@link module:engine/field-spec.readField}'s job, and it applies the same
111
+ * coercion wherever the value was authored.
112
+ *
113
+ * @param {import("./field-spec.mjs").FieldSpec} field - The declaration.
114
+ * @param {object} fm - The note's frontmatter.
115
+ * @param {object} [options] - Options.
116
+ * @param {string} [options.block="sohl"] - The system block to resolve against.
117
+ * @returns {{value: any, from: FieldSource}} The raw value and where it came
118
+ * from.
119
+ */
120
+ export function resolveFieldValue(field: import("./field-spec.mjs").FieldSpec, fm: object, { block }?: {
121
+ block?: string | undefined;
122
+ }): {
123
+ value: any;
124
+ from: FieldSource;
125
+ };
126
+ /**
127
+ * Every path a note authors under `<system>.system`, containers included.
128
+ *
129
+ * A container is listed as well as its leaves because a published schema
130
+ * declares both — `charges` as a `SchemaField` and `charges.value` beneath it —
131
+ * so a check that knew only the leaves could not tell a misspelled container
132
+ * from a misspelled leaf.
133
+ *
134
+ * An **empty** mapping is a leaf: `body: {}` is a value the author wrote, and
135
+ * walking into it would make it vanish.
136
+ *
137
+ * @param {Record<string, unknown>} data - The authored `system` data.
138
+ * @param {string} [prefix] - Internal: the path so far.
139
+ * @returns {string[]} The dotted paths.
140
+ */
141
+ export function systemDataPaths(data: Record<string, unknown>, prefix?: string): string[];
142
+ /**
143
+ * The authored paths a system's published schema does not declare.
144
+ *
145
+ * **Reported at the shallowest undeclared path.** Everything beneath an
146
+ * undeclared container is undeclared by construction, so listing it all buries
147
+ * the one mistake in a wall of consequences — one finding per typo is what an
148
+ * author can act on.
149
+ *
150
+ * Foundry discards an unknown `system` key at construction and says nothing, so
151
+ * this is the difference between "the field is lost at load" and "the build
152
+ * told you where".
153
+ *
154
+ * @param {Record<string, unknown>} data - The authored `system` data.
155
+ * @param {ReadonlySet<string>} declared - Every field path the schema declares
156
+ * for this subtype, inherited ones included.
157
+ * @param {string} [prefix] - Internal: the path so far.
158
+ * @returns {string[]} The undeclared paths, shallowest-first.
159
+ */
160
+ export function undeclaredPaths(data: Record<string, unknown>, declared: ReadonlySet<string>, prefix?: string): string[];
161
+ /**
162
+ * Keys directly under a system block that neither this format nor the system
163
+ * recognizes.
164
+ *
165
+ * Until now an unrecognized key under `sohl:` was reported only against SoHL's
166
+ * *field* vocabulary, and a key under any other system's block was not looked
167
+ * at at all — dropped in silence, which is the failure class the frontmatter
168
+ * lint exists for.
169
+ *
170
+ * @param {object} fm - The note's frontmatter.
171
+ * @param {string} block - The block key.
172
+ * @param {object} options - Options.
173
+ * @param {Iterable<string>} options.known - The keys this system declares on
174
+ * top of the shared vocabulary: its generators, its toolchain keys, and —
175
+ * until #126 moves them — the field names its notes still author in the
176
+ * block.
177
+ * @returns {string[]} The unrecognized keys, in authored order.
178
+ */
179
+ export function unknownBlockKeys(fm: object, block: string, { known }: {
180
+ known: Iterable<string>;
181
+ }): string[];
182
+ /**
183
+ * The `system` paths a field declaration writes, for {@link mergeSystemData}.
184
+ *
185
+ * Exactly each field's `to`, and deliberately **not** its ancestors: a field
186
+ * writing `locations.flexible` does not own `locations`, and claiming the
187
+ * container would make an authored `locations.facing` disappear — a silent drop
188
+ * inside the mechanism built to stop them.
189
+ *
190
+ * @param {readonly {to?: string}[]} [fields] - A type's field declaration.
191
+ * @returns {Set<string>} The claimed destinations.
192
+ */
193
+ export function claimedPaths(fields?: readonly {
194
+ to?: string;
195
+ }[]): Set<string>;
196
+ /**
197
+ * Merge a note's `<system>.system` onto a built `system` block, verbatim.
198
+ *
199
+ * **Verbatim means the paths are the schema's, not that the merge is a
200
+ * replacement.** A container the builder already wrote is merged into rather
201
+ * than overwritten, so authoring one leaf of `body` does not silently discard
202
+ * the rest of it.
203
+ *
204
+ * A path a **declared field** already claims is left alone. That field's value
205
+ * came from the same authored place, through the field's own `read`; writing it
206
+ * again uncoerced would make the coercion depend on which of two mechanisms ran
207
+ * last — the drift a single statement of the mapping exists to prevent.
208
+ *
209
+ * @param {object} built - The `system` block the builder produced (mutated).
210
+ * @param {object} fm - The note's frontmatter.
211
+ * @param {object} options - Options.
212
+ * @param {string} options.block - The block key.
213
+ * @param {ReadonlySet<string>} [options.claimed] - Paths a declared field
214
+ * writes, which this merge leaves to it.
215
+ * @returns {object} `built`, for chaining.
216
+ */
217
+ export function mergeSystemData(built: object, fm: object, { block, claimed }: {
218
+ block: string;
219
+ claimed?: ReadonlySet<string> | undefined;
220
+ }): object;
221
+ /**
222
+ * The key inside a system block that maps onto the document's `system`
223
+ * property.
224
+ *
225
+ * Named rather than spelled inline: it is the one key whose contents are the
226
+ * *system's* vocabulary rather than this format's, and every check that has to
227
+ * treat it differently reads it from here.
228
+ *
229
+ * @type {string}
230
+ */
231
+ export const SYSTEM_DATA_KEY: string;
232
+ /**
233
+ * The properties inside a system block that map onto a document property, block
234
+ * key → document key.
235
+ *
236
+ * The two names are equal in every row today, and are written out anyway for
237
+ * the reason every row of a document-subtype map is: a mapping that exists only
238
+ * because two vocabularies happen to be spelled alike is not a mapping.
239
+ *
240
+ * `effects` is **plural**, matching both the existing top-level frontmatter
241
+ * field (authored on 24 notes in `sohl-kethira-basic`) and the Foundry document
242
+ * property. A singular-to-plural rename applying to one property and not its
243
+ * neighbour reads as a typo for years.
244
+ *
245
+ * `items` exists on **actors only**; an item document has no embedded items,
246
+ * and a note declaring it under a block whose subtype is an Item is authoring
247
+ * something nothing will read.
248
+ *
249
+ * @type {Readonly<Record<string, string>>}
250
+ */
251
+ export const BLOCK_DOCUMENT_PROPERTIES: Readonly<Record<string, string>>;
252
+ /**
253
+ * Block keys that are **build directives** — they tell the toolchain how to
254
+ * build the document and land on no document property at all.
255
+ *
256
+ * `pack` is the first and, so far, the only one: it names the compendium this
257
+ * system's document is compiled into, which is what makes "one note, several
258
+ * packs" expressible per system. Its shared top-level form already exists
259
+ * (`PACK_FIELD` in `pack-router.mjs`, authored on 352 notes), so the override
260
+ * rule gives it a per-system form for free.
261
+ *
262
+ * @type {readonly string[]}
263
+ */
264
+ export const BLOCK_DIRECTIVES: readonly string[];
265
+ /**
266
+ * Every key any system block may carry, whatever the system.
267
+ *
268
+ * A system adds its own on top — `archetype`, `kbcat` and the generators — and
269
+ * names them where it declares its vocabulary; see {@link unknownBlockKeys}.
270
+ *
271
+ * @type {ReadonlySet<string>}
272
+ */
273
+ export const SYSTEM_BLOCK_KEYS: ReadonlySet<string>;
274
+ /**
275
+ * Where a declared field's value came from.
276
+ *
277
+ * Reported alongside the value so a caller — a linter, a migration, a test —
278
+ * can distinguish a value an author wrote from one a default supplied, which
279
+ * the value alone never says.
280
+ */
281
+ export type FieldSource = "system" | "block" | "shared" | "default" | "value";
@@ -30,18 +30,27 @@ export function frontmatterWikilinks(fm: unknown): Array<{
30
30
  /**
31
31
  * Rewrites the wikilinks in a markdown body as KB-local markdown links.
32
32
  *
33
- * A target is looked up case-insensitively: first as an alias scoped to the
34
- * source's own **type** (`ctx.typeAlias`, keyed `type|alias`) — a note's
35
- * directory and `category` play no part — then in the KB-wide `ctx.index` (keyed
36
- * by the unambiguous `section/slug` and `type/shortcode`, plus name/filename/slug
37
- * fallbacks).
33
+ * A target is looked up case-insensitively in **one** of two namespaces, and
34
+ * the pipe chooses which (#131):
38
35
  *
39
- * An unresolved target fails the build only when it is a genuine intra-KB
40
- * problem an ambiguous alias, or a qualified `prefix/key` whose prefix is a
41
- * real KB section or content directory. Anything else is treated as an external
42
- * referenceuntil every package's manifest is present, after which any
43
- * `type-shortcode` address resolving nowhere fails too. Failures are collected
44
- * in `ctx.errors`.
36
+ * - **Unpiped** an alias scoped to the source's own **type**
37
+ * (`ctx.typeAlias`, keyed `type|alias`). A note's directory and `category`
38
+ * play no part.
39
+ * - **Piped** an address, parsed by {@link readQualifier} and looked up in
40
+ * the KB-wide `ctx.index` (the canonical `package-type-shortcode`,
41
+ * `type/shortcode`, and the site's own `section/slug`), then in the vendored
42
+ * `ctx.foreign` manifests.
43
+ *
44
+ * Neither falls back to the other, so the name/basename/slug fallbacks that
45
+ * share `ctx.index` no longer answer for an address: only a slash-qualified
46
+ * target reaches the raw key, which is what keeps `section/slug` addressable.
47
+ *
48
+ * An unresolved target fails the build when it is a genuine intra-KB problem —
49
+ * an ambiguous alias, a qualified `prefix/key` whose prefix is a real KB
50
+ * section or content directory, or a **piped** target that is not an address
51
+ * at all. Anything else is treated as an external reference — until every
52
+ * package's manifest is present, after which any address resolving nowhere
53
+ * fails too. Failures are collected in `ctx.errors`.
45
54
  *
46
55
  * Whether or not it fails the build, a target that resolves nowhere renders
47
56
  * through {@link unresolvedLink} rather than as bare prose (#1665): the author's
@@ -56,7 +65,9 @@ export function frontmatterWikilinks(fm: unknown): Array<{
56
65
  *
57
66
  * @param {string} body - The markdown body.
58
67
  * @param {object} ctx - `{ index, typeAlias, collide, typeCollide, sections,
59
- * contentTypes, foreign, manifestsComplete, type, errors, src }`. `foreign`
68
+ * contentTypes, packages, foreign, manifestsComplete, type, errors, src }`.
69
+ * `packages` is every package an address may name, without which the leading
70
+ * package segment of a canonical address reads as an unknown type; `foreign`
60
71
  * is the cross-package manifest index (#1446); `manifestsComplete` says
61
72
  * whether every linkable package is accounted for. Together they decide
62
73
  * whether an unresolved address is a typo or a package merely absent.
@@ -47,6 +47,35 @@ export function parseWikilink(rawInner: string): ParsedWikilink;
47
47
  export function authoredLabel({ display }: {
48
48
  display: string | null;
49
49
  }): string | null;
50
+ /**
51
+ * Which namespace a link resolves in: the **address** space, or the **alias**
52
+ * space.
53
+ *
54
+ * **The pipe decides, and nothing else does** (#131). `[[x|…]]` is an address,
55
+ * parsed by the address grammar; `[[x]]` is an alias, looked up within the
56
+ * source note's own type. Neither falls back to the other.
57
+ *
58
+ * Both resolvers used to decide by *shape* instead — try the address, fall
59
+ * back to the alias — which had three costs. An author could not say which
60
+ * they meant, so a note whose **name** looked like an address (`Grukar-ahk`)
61
+ * was read as one, and a genuine address that resolved nowhere silently became
62
+ * a name lookup and reported nothing. And a positional address grammar could
63
+ * not split a target confidently until it had first ruled out every note name
64
+ * in the corpus.
65
+ *
66
+ * An **empty** label is still a pipe: `[[x|]]` is an address that renders the
67
+ * target's *current* name, so a rename shows at every citation with no link
68
+ * edited. That is why this reads {@link ParsedWikilink.labelled} and not
69
+ * {@link authoredLabel} — the two answer different questions, and only one of
70
+ * them is about namespaces.
71
+ *
72
+ * @param {{labelled: boolean}} parsed - A parsed wikilink, or anything
73
+ * carrying its `labelled`.
74
+ * @returns {boolean} True when the target is an address.
75
+ */
76
+ export function resolvesAsAddress({ labelled }: {
77
+ labelled: boolean;
78
+ }): boolean;
50
79
  /**
51
80
  * Whether a parsed link addresses a section of the page it is written on.
52
81
  *
@@ -1,4 +1,17 @@
1
1
  export class Actors extends BasePackCompiler {
2
+ /**
3
+ * The note-type → document-subtype map this pass compiles against.
4
+ *
5
+ * Stated by the class rather than reached for through the module import, so
6
+ * every subtype decision the pass makes — the actor's own, and each
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.
11
+ *
12
+ * @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
13
+ */
14
+ static documentSubtypes: import("../engine/document-subtypes.mjs").DocumentSubtypeMap;
2
15
  constructor({ itemsSourceDirs, foreignSourceDirs, ...options }: {
3
16
  [x: string]: any;
4
17
  itemsSourceDirs?: never[] | undefined;
@@ -10,21 +23,63 @@ export class Actors extends BasePackCompiler {
10
23
  itemsMap: Map<string, object> | undefined;
11
24
  /** @inheritdoc */
12
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;
13
49
  /**
14
50
  * Resolve one embedded item from a `(type, shortcode?, overlay)`
15
51
  * descriptor. If `shortcode` is given, the predefined item is fetched
16
52
  * from `itemsMap` and the overlay deep-merged on top. If absent, the
17
53
  * descriptor must carry enough fields to stand alone. The embedded
18
54
  * item's `_id` is regenerated deterministically from
19
- * `(actorId, type, shortcode, indexKey)` so re-exports are stable.
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.
20
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.
21
74
  */
22
- resolveEmbedded(itemsMap: any, actorId: any, type: any, shortcode: any, overlay: any, indexKey: any, ctx: any): any;
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;
23
78
  /**
24
79
  * Build all embedded items for an actor: one per `sohl.attributes`
25
80
  * entry plus one per `sohl.items` entry. `sohl.skills` is ignored.
26
81
  */
27
- buildEmbeddedItems(itemsMap: any, actorId: any, fm: any, ctx: any): any[];
82
+ buildEmbeddedItems(itemsMap: any, actorId: any, fm: any, ctx: any): object[];
28
83
  /**
29
84
  * Bake each unopened skill's opening mastery level into the document (#46).
30
85
  *
@@ -59,11 +114,12 @@ export class Actors extends BasePackCompiler {
59
114
  _id: any;
60
115
  system: {
61
116
  shortcode: any;
117
+ archetype: number | null;
62
118
  portrait: string;
63
119
  appearance: string;
64
120
  dossier: string;
65
121
  };
66
- items: any[];
122
+ items: object[];
67
123
  prototypeToken: {
68
124
  name: any;
69
125
  displayName: number;
@@ -78,13 +134,13 @@ export class Actors extends BasePackCompiler {
78
134
  };
79
135
  detectionModes: never[];
80
136
  };
81
- effects: never[];
137
+ effects: any[];
82
138
  folder: string | null;
83
139
  sort: number;
84
140
  ownership: {
85
141
  default: number;
86
142
  };
87
- flags: object;
143
+ flags: any;
88
144
  _stats: object;
89
145
  _key: string;
90
146
  };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Every content type SoHL compiles into a Foundry document, and what it
3
+ * becomes.
4
+ *
5
+ * The Item rows are the thirteen types the item registry declares; the one
6
+ * Actor row is `being`, which was two types (`character` and `creature`)
7
+ * compiling to the same actor until they were retired in SoHL#1580. Types this
8
+ * map does not name — `doc`, `macro`, the three map types — compile into
9
+ * documents that carry no system subtype at all, so they have no row and never
10
+ * needed one.
11
+ *
12
+ * @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
13
+ */
14
+ export const SOHL_DOCUMENT_SUBTYPES: import("../engine/document-subtypes.mjs").DocumentSubtypeMap;
@@ -1,4 +1,5 @@
1
1
  export * as itemBuilders from "./item-builders.mjs";
2
+ export * as documentSubtypes from "./document-subtypes.mjs";
2
3
  export * as items from "./items.mjs";
3
4
  export * as actors from "./actors.mjs";
4
5
  export * as kbPasses from "./kb-passes.mjs";
@@ -7,6 +7,27 @@ export class Items extends BasePackCompiler {
7
7
  * @type {Record<string, number>}
8
8
  */
9
9
  counts: Record<string, number>;
10
+ /**
11
+ * The Foundry Item subtype a note compiles into.
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).
26
+ *
27
+ * @param {object} fm - The note's frontmatter.
28
+ * @returns {string} The document's `type`.
29
+ */
30
+ itemSubtype(fm: object): string;
10
31
  /** An item is named by its own type in the log, not by "item". */
11
32
  noteLabel(fm: any): any;
12
33
  /** @inheritdoc */