@heroiclands/package-build 8.1.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +748 -0
- package/CONTENT.md +273 -13
- package/bin/content-build.mjs +479 -123
- package/bin/package-build.mjs +27 -69
- package/bin/report.mjs +1 -2
- package/bundle.mjs +2 -10
- package/config.mjs +31 -106
- package/container.mjs +13 -57
- package/content-config.mjs +300 -188
- package/coverage.mjs +14 -55
- package/deploy.mjs +4 -13
- package/docs/content-format.md +1418 -0
- package/e2e.mjs +16 -55
- package/engine/address-charset.mjs +62 -0
- package/engine/address-diff.mjs +1 -4
- package/engine/alias-index.mjs +153 -0
- package/engine/base-compiler.mjs +203 -31
- package/engine/code-fences.mjs +4 -13
- package/engine/compendiums.mjs +13 -37
- package/engine/content-address.mjs +6 -10
- package/engine/content-format-check.mjs +570 -0
- package/engine/content-format.mjs +253 -0
- package/engine/content-links.mjs +144 -99
- package/engine/content-lint.mjs +12 -16
- package/engine/content-slug.mjs +2 -6
- package/engine/content-tables.mjs +26 -79
- package/engine/diagnostics.mjs +37 -15
- package/engine/document-subtypes.mjs +440 -0
- package/engine/field-reference.mjs +6 -20
- package/engine/field-spec.mjs +49 -45
- package/engine/foreign-catalog.mjs +7 -22
- package/engine/foreign-manifests.mjs +1 -4
- package/engine/frontmatter-lint.mjs +347 -43
- package/engine/frontmatter.mjs +3 -8
- package/engine/generate.mjs +36 -20
- package/engine/helpers.mjs +54 -81
- package/engine/homepage.mjs +4 -15
- package/engine/ids.mjs +21 -12
- package/engine/index.mjs +15 -0
- package/engine/item-registry.mjs +72 -9
- package/engine/journals.mjs +4 -14
- package/engine/kb-manifest.mjs +41 -24
- package/engine/macros.mjs +2 -10
- package/engine/manifest-emit.mjs +6 -17
- package/engine/map-notes.mjs +53 -87
- package/engine/note-claims.mjs +383 -0
- package/engine/note-package.mjs +1 -4
- package/engine/note-vocabulary.mjs +678 -0
- package/engine/pack-config.mjs +56 -60
- package/engine/pack-router.mjs +18 -8
- package/engine/prose-config.mjs +20 -4
- package/engine/prose-lint.mjs +61 -17
- package/engine/region-events.mjs +1 -3
- package/engine/retired-fields.mjs +117 -3
- package/engine/scene-levels.mjs +8 -22
- package/engine/scenes.mjs +31 -47
- package/engine/schema-check.mjs +348 -7
- package/engine/schema-extract.mjs +11 -39
- package/engine/site-build.mjs +13 -38
- package/engine/site-index.mjs +40 -35
- package/engine/system-block.mjs +513 -0
- package/engine/web-wikilinks.mjs +115 -92
- package/engine/wikilink-syntax.mjs +30 -0
- package/engine/wikilinks.mjs +147 -183
- package/index.mjs +1 -5
- package/lang.mjs +1 -3
- package/manifest.mjs +10 -37
- package/markdownlint-config.mjs +1 -5
- package/package.json +6 -2
- package/sohl/actors.mjs +251 -68
- package/sohl/being-info.mjs +3 -6
- package/sohl/document-subtypes.mjs +82 -0
- package/sohl/index.mjs +4 -6
- package/sohl/item-builders.mjs +1 -3
- package/sohl/item-fields.mjs +16 -34
- package/sohl/items.mjs +111 -17
- package/sohl/kb-passes.mjs +29 -39
- package/sohl/note-schemas.mjs +11 -7
- package/sohl/skill-base.mjs +7 -23
- package/stage.mjs +3 -13
- package/templates.mjs +4 -15
- package/types/bundle.d.mts +1 -1
- package/types/container.d.mts +2 -2
- package/types/content-config.d.mts +48 -4
- package/types/coverage.d.mts +1 -1
- package/types/e2e.d.mts +4 -4
- package/types/engine/address-charset.d.mts +45 -0
- package/types/engine/alias-index.d.mts +122 -0
- package/types/engine/base-compiler.d.mts +132 -4
- package/types/engine/content-address.d.mts +2 -2
- package/types/engine/content-format-check.d.mts +163 -0
- package/types/engine/content-format.d.mts +101 -0
- package/types/engine/content-links.d.mts +16 -1
- package/types/engine/content-lint.d.mts +6 -0
- package/types/engine/diagnostics.d.mts +29 -0
- package/types/engine/document-subtypes.d.mts +233 -0
- package/types/engine/field-spec.d.mts +76 -23
- package/types/engine/frontmatter-lint.d.mts +47 -2
- package/types/engine/generate.d.mts +15 -2
- package/types/engine/helpers.d.mts +22 -14
- package/types/engine/ids.d.mts +10 -0
- package/types/engine/index.d.mts +5 -0
- package/types/engine/item-registry.d.mts +21 -2
- package/types/engine/kb-manifest.d.mts +35 -8
- package/types/engine/map-notes.d.mts +21 -11
- package/types/engine/note-claims.d.mts +113 -0
- package/types/engine/note-vocabulary.d.mts +251 -0
- package/types/engine/pack-config.d.mts +4 -3
- package/types/engine/pack-router.d.mts +4 -4
- package/types/engine/prose-lint.d.mts +6 -2
- package/types/engine/retired-fields.d.mts +73 -2
- package/types/engine/schema-check.d.mts +182 -0
- package/types/engine/schema-extract.d.mts +1 -1
- package/types/engine/site-index.d.mts +1 -1
- package/types/engine/system-block.d.mts +281 -0
- package/types/engine/web-wikilinks.d.mts +23 -12
- package/types/engine/wikilink-syntax.d.mts +29 -0
- package/types/manifest.d.mts +1 -1
- package/types/sohl/actors.d.mts +62 -6
- package/types/sohl/document-subtypes.d.mts +14 -0
- package/types/sohl/index.d.mts +1 -0
- 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
|
|
34
|
-
*
|
|
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
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* in
|
|
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 }`.
|
|
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
|
*
|
package/types/manifest.d.mts
CHANGED
|
@@ -182,7 +182,7 @@ export function buildManifest({ config, packageJson, artifact, flags }: {
|
|
|
182
182
|
* @throws {Error} When a `packFolders` entry names a pack the package does not
|
|
183
183
|
* ship. Nothing is written in that case.
|
|
184
184
|
*/
|
|
185
|
-
export function writeManifest({ config, packageJson, artifact, outDir, flags, configFile
|
|
185
|
+
export function writeManifest({ config, packageJson, artifact, outDir, flags, configFile }: {
|
|
186
186
|
config: object;
|
|
187
187
|
packageJson: object;
|
|
188
188
|
artifact: string;
|
package/types/sohl/actors.d.mts
CHANGED
|
@@ -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,
|
|
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:
|
|
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):
|
|
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:
|
|
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:
|
|
137
|
+
effects: any[];
|
|
82
138
|
folder: string | null;
|
|
83
139
|
sort: number;
|
|
84
140
|
ownership: {
|
|
85
141
|
default: number;
|
|
86
142
|
};
|
|
87
|
-
flags:
|
|
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;
|
package/types/sohl/index.d.mts
CHANGED
package/types/sohl/items.d.mts
CHANGED
|
@@ -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 */
|