@heroiclands/package-build 22.9.1 → 22.10.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 +54 -0
- package/CONTENT.md +33 -0
- package/bin/content-build.mjs +3 -1
- package/docs/api.md +10 -6
- package/docs/content-format.md +384 -200
- package/docs/project-setup.md +23 -0
- package/engine/actor-compiler.mjs +20 -7
- package/engine/address-fields.mjs +404 -0
- package/engine/address-values.mjs +80 -0
- package/engine/address.mjs +769 -0
- package/engine/art-fields.mjs +112 -97
- package/engine/art-slots.mjs +81 -0
- package/engine/base-compiler.mjs +59 -7
- package/engine/bundle-notes.mjs +5 -2
- package/engine/bundles.mjs +17 -10
- package/engine/content-address.mjs +23 -186
- package/engine/content-embeds.mjs +18 -4
- package/engine/content-format-check.mjs +50 -22
- package/engine/content-index.mjs +85 -43
- package/engine/content-links.mjs +105 -104
- package/engine/content-lint.mjs +11 -1
- package/engine/content-tables.mjs +9 -1
- package/engine/field-spec.mjs +20 -4
- package/engine/folder-notes.mjs +44 -15
- package/engine/foundry-entries.mjs +3 -0
- package/engine/frontmatter-lint.mjs +144 -43
- package/engine/frontmatter.mjs +55 -10
- package/engine/generate.mjs +7 -1
- package/engine/helpers.mjs +37 -9
- package/engine/holdings.mjs +41 -12
- package/engine/index-records.mjs +2 -0
- package/engine/infobox-render.mjs +2 -1
- package/engine/infobox.mjs +26 -6
- package/engine/item-compiler.mjs +5 -0
- package/engine/item-docs.mjs +7 -44
- package/engine/map-dot.mjs +2 -0
- package/engine/map-notes.mjs +7 -2
- package/engine/map-places.mjs +42 -10
- package/engine/metadata-index.mjs +60 -5
- package/engine/note-addresses.mjs +310 -0
- package/engine/note-vocabulary.mjs +145 -26
- package/engine/pdf-build.mjs +1 -1
- package/engine/pdf-toc.mjs +17 -2
- package/engine/place-relations.mjs +160 -50
- package/engine/populations.mjs +17 -9
- package/engine/reference-targets.mjs +54 -0
- package/engine/scenes.mjs +5 -2
- package/engine/shortcode-references.mjs +68 -0
- package/engine/site-build.mjs +39 -15
- package/engine/site-index.mjs +57 -75
- package/engine/sql-tables.mjs +43 -9
- package/engine/system-block.mjs +21 -0
- package/engine/system-vocabulary.mjs +46 -0
- package/engine/web-wikilinks.mjs +11 -9
- package/engine/wikilink-syntax.mjs +21 -0
- package/engine/wikilinks.mjs +92 -306
- package/githooks/attribution.mjs +82 -0
- package/githooks/commit-message.mjs +26 -0
- package/githooks/commit-msg +3 -35
- package/hm3/actors.mjs +8 -24
- package/hm3/item-fields.mjs +15 -5
- package/package.json +1 -1
- package/sohl/infobox.mjs +80 -17
- package/sohl/item-fields.mjs +114 -41
- package/types/engine/address-fields.d.mts +90 -0
- package/types/engine/address-values.d.mts +34 -0
- package/types/engine/address.d.mts +452 -0
- package/types/engine/art-fields.d.mts +59 -53
- package/types/engine/art-slots.d.mts +40 -0
- package/types/engine/base-compiler.d.mts +24 -2
- package/types/engine/bundle-notes.d.mts +2 -2
- package/types/engine/content-address.d.mts +3 -139
- package/types/engine/content-embeds.d.mts +1 -1
- package/types/engine/content-format-check.d.mts +25 -0
- package/types/engine/content-index.d.mts +2 -2
- package/types/engine/content-links.d.mts +6 -5
- package/types/engine/field-spec.d.mts +44 -9
- package/types/engine/folder-notes.d.mts +11 -10
- package/types/engine/frontmatter-lint.d.mts +7 -97
- package/types/engine/frontmatter.d.mts +16 -1
- package/types/engine/helpers.d.mts +2 -2
- package/types/engine/infobox.d.mts +6 -0
- package/types/engine/item-docs.d.mts +1 -35
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/note-addresses.d.mts +41 -0
- package/types/engine/note-vocabulary.d.mts +20 -3
- package/types/engine/place-relations.d.mts +12 -1
- package/types/engine/populations.d.mts +17 -2
- package/types/engine/reference-targets.d.mts +9 -0
- package/types/engine/shortcode-references.d.mts +14 -0
- package/types/engine/site-build.d.mts +3 -3
- package/types/engine/site-index.d.mts +9 -30
- package/types/engine/sql-tables.d.mts +2 -2
- package/types/engine/system-block.d.mts +17 -0
- package/types/engine/system-vocabulary.d.mts +16 -0
- package/types/engine/wikilink-syntax.d.mts +7 -0
- package/types/engine/wikilinks.d.mts +6 -109
- package/types/hm3/item-fields.d.mts +5 -0
- package/types/sohl/infobox.d.mts +4 -5
- package/types/sohl/item-fields.d.mts +8 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A complete Address: four segments, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* `type` is the segment the canonical string carries, after the `doc<type>`
|
|
5
|
+
* redirect. There is no fifth field recording the written form, because the
|
|
6
|
+
* written form is a rendering rather than part of the value.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {object} AddressTuple
|
|
9
|
+
* @property {string} package - The owning **content** package.
|
|
10
|
+
* @property {string} system - The system whose document this addresses, or
|
|
11
|
+
* `none`.
|
|
12
|
+
* @property {string} type - The type segment of the canonical Address.
|
|
13
|
+
* @property {string} shortcode - The note's `shortcode`.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Why a written value is not an Address.
|
|
17
|
+
*
|
|
18
|
+
* The vocabulary is shared with the resolvers, so one authored mistake gets one
|
|
19
|
+
* verdict however many surfaces report it.
|
|
20
|
+
*
|
|
21
|
+
* - `not-an-address` — the value does not parse as one, which for body prose is
|
|
22
|
+
* an ordinary note name rather than a mistake.
|
|
23
|
+
* - `no-type` — the `<type>` segment is omitted and the position declares no
|
|
24
|
+
* default to fill it in, so nothing says what the value names.
|
|
25
|
+
* - `unknown-type` — the value is definitely qualified and names no known type.
|
|
26
|
+
* - `no-content-index` — the package named publishes no content index, so there
|
|
27
|
+
* is nothing to resolve the rest of the target against.
|
|
28
|
+
* - `not-lowercase` — a package, system or type segment carries a capital.
|
|
29
|
+
* - `invalid-segment` — a declared Address contains a malformed segment.
|
|
30
|
+
* - `no-package` — a declared Address cannot supply its package.
|
|
31
|
+
* - `invalid-system` — a declared Address cannot supply a registered system.
|
|
32
|
+
*
|
|
33
|
+
* @typedef {{reason: string, package?: string}} AddressProblem
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Every content type whose **prose compiles into a JournalEntry of its own**,
|
|
37
|
+
* addressed by the virtual `doc<type>` qualifier.
|
|
38
|
+
*
|
|
39
|
+
* Every item type, every actor type, every map type, plus `macro` — a macro
|
|
40
|
+
* note's body documents the script the note also compiles into a Macro, which
|
|
41
|
+
* is the same shape as an item and its description: one note, two documents,
|
|
42
|
+
* the prose living in the journals pack.
|
|
43
|
+
*
|
|
44
|
+
* **One set, read by the compilers and by the grammar alike.** The journals
|
|
45
|
+
* pass decides what to compile from it, the link manifest decides what to
|
|
46
|
+
* publish a `doc<type>` entry for, and the parser decides whether `doc<type>`
|
|
47
|
+
* is a legal type segment. Held apart, the three drift into a manifest that
|
|
48
|
+
* asserts documentation nothing compiled, a compiled entry no consumer can
|
|
49
|
+
* address, or an authored `doc<type>` link that stops parsing. It is composed
|
|
50
|
+
* exactly once, in `defineConfig`, and read from there — never recomposed at a
|
|
51
|
+
* call site.
|
|
52
|
+
*
|
|
53
|
+
* `doc` notes are absent, for the reason that applies to them alone: a `doc`
|
|
54
|
+
* note's single document *is* its prose, so there is no second document for a
|
|
55
|
+
* `doc<type>` address to name.
|
|
56
|
+
*
|
|
57
|
+
* An accessor rather than a hoisted constant, so that importing this module
|
|
58
|
+
* needs no configuration.
|
|
59
|
+
*
|
|
60
|
+
* @returns {ReadonlySet<string>} The configured doc-carrying types.
|
|
61
|
+
*/
|
|
62
|
+
export function docEntryTypes(): ReadonlySet<string>;
|
|
63
|
+
/**
|
|
64
|
+
* Whether a content note's type is one whose prose becomes a JournalEntry of
|
|
65
|
+
* its own.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} type - The note's `type` frontmatter.
|
|
68
|
+
* @returns {boolean} True for an item type, an actor type, a map type and
|
|
69
|
+
* `macro`; false for `doc`.
|
|
70
|
+
*/
|
|
71
|
+
export function hasDocEntry(type: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Reads a qualifier as the **virtual `doc<type>`** form, or reports that it is
|
|
74
|
+
* not one.
|
|
75
|
+
*
|
|
76
|
+
* A document and its documentation are two documents in two packs, so they need
|
|
77
|
+
* two addresses. `skill-wpnc` is the item; `docskill-wpnc` is the JournalEntry
|
|
78
|
+
* its prose compiled into, and `docmacro-autoattack` is the same arrangement
|
|
79
|
+
* for a macro.
|
|
80
|
+
*
|
|
81
|
+
* The virtual form exists for a type that carries separate documentation
|
|
82
|
+
* ({@link docEntryTypes} — the set the journals compiler and the link manifest
|
|
83
|
+
* read too), **or** for one that routes to the items pack. The second clause
|
|
84
|
+
* covers the open, unenumerated set: types that compile into items are not
|
|
85
|
+
* enumerated anywhere, and a foreign package may publish an item type this
|
|
86
|
+
* build has never heard of. Dropping it would silently unlink every `doc<type>`
|
|
87
|
+
* address into such a package.
|
|
88
|
+
*
|
|
89
|
+
* A **real** type of the same name always wins: the virtual reading is only
|
|
90
|
+
* consulted for a qualifier no authored note claims.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} qualifier - The already-normalised type segment.
|
|
93
|
+
* @param {Set<string>} types - Every type the content tree contains.
|
|
94
|
+
* @returns {string|null} The underlying document type, or `null` when the
|
|
95
|
+
* qualifier is not a virtual one.
|
|
96
|
+
*/
|
|
97
|
+
export function resolveItemDocType(qualifier: string, types: Set<string>): string | null;
|
|
98
|
+
/**
|
|
99
|
+
* The vocabularies a written Address is read against.
|
|
100
|
+
*
|
|
101
|
+
* These are the tree's, not the position's — every position reads against the
|
|
102
|
+
* same ones — and they travel beside the defaults so that reading an Address is
|
|
103
|
+
* one call.
|
|
104
|
+
*
|
|
105
|
+
* @typedef {object} AddressVocabulary
|
|
106
|
+
* @property {Set<string>} [types] - Every type the content tree contains.
|
|
107
|
+
* @property {Set<string>} [packages] - Every package an Address may name.
|
|
108
|
+
* Omitted by callers that resolve within one package, where the fully
|
|
109
|
+
* qualified form cannot occur.
|
|
110
|
+
* @property {Set<string>} [noIndexPackages] - Packages declared
|
|
111
|
+
* `contentIndex: false` — a Foundry dependency only, with no fetched index. A
|
|
112
|
+
* fully qualified target naming one is refused with `no-content-index` before
|
|
113
|
+
* its type is even considered.
|
|
114
|
+
*/
|
|
115
|
+
/**
|
|
116
|
+
* What a position contributes: the defaults for the segments an author may omit.
|
|
117
|
+
*
|
|
118
|
+
* `package` and `system` are always supplied; `type` is supplied only by a
|
|
119
|
+
* position that accepts exactly one, and a position accepting several declares
|
|
120
|
+
* none, so a value omitting the `<type>` segment is refused rather than guessed
|
|
121
|
+
* at.
|
|
122
|
+
*
|
|
123
|
+
* @typedef {AddressVocabulary & {package?: string, system?: string,
|
|
124
|
+
* type?: string}} AddressDefaults
|
|
125
|
+
*/
|
|
126
|
+
/**
|
|
127
|
+
* Parse a written Address into the one complete tuple it names.
|
|
128
|
+
*
|
|
129
|
+
* **An omitted segment defaults from where the value is written** — it is not a
|
|
130
|
+
* wildcard, and resolution is not a search. So every short form has exactly one
|
|
131
|
+
* expansion, computed before anything is looked up, and there is no candidate
|
|
132
|
+
* set to disambiguate.
|
|
133
|
+
*
|
|
134
|
+
* @param {unknown} written - The value as authored, with any Wikilink anchor and
|
|
135
|
+
* label already removed.
|
|
136
|
+
* @param {AddressDefaults} [defaults] - The position's defaults and the tree's
|
|
137
|
+
* vocabularies.
|
|
138
|
+
* @param {{declared?: boolean, legacyShortcodeCase?: boolean}} [options] - A declared Address validates every
|
|
139
|
+
* segment and requires complete defaults. Package membership is a separate
|
|
140
|
+
* resolution question in this mode; prose recognition keeps its vocabulary checks.
|
|
141
|
+
* `legacyShortcodeCase` accepts case-insensitive model Shortcodes while the
|
|
142
|
+
* completed tuple contains their canonical lowercase identity.
|
|
143
|
+
* @returns {AddressTuple|AddressProblem} The complete Address, or why there is
|
|
144
|
+
* none.
|
|
145
|
+
*/
|
|
146
|
+
export function parseAddress(written: unknown, defaults?: AddressDefaults, { declared, legacyShortcodeCase }?: {
|
|
147
|
+
declared?: boolean;
|
|
148
|
+
legacyShortcodeCase?: boolean;
|
|
149
|
+
}): AddressTuple | AddressProblem;
|
|
150
|
+
/**
|
|
151
|
+
* Whether a value is an internally parsed Address.
|
|
152
|
+
* @param {unknown} value - A property value.
|
|
153
|
+
* @returns {boolean} Whether it is a typed tuple.
|
|
154
|
+
*/
|
|
155
|
+
export function isAddressTuple(value: unknown): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Render a complete Address as its canonical string.
|
|
158
|
+
*
|
|
159
|
+
* Lowercased, because an Address has one spelling: two names differing only in
|
|
160
|
+
* case are two names nobody can tell apart.
|
|
161
|
+
*
|
|
162
|
+
* @param {AddressTuple} tuple - The complete Address.
|
|
163
|
+
* @returns {string} `package-system-type-shortcode`.
|
|
164
|
+
*/
|
|
165
|
+
export function renderAddress(tuple: AddressTuple): string;
|
|
166
|
+
/**
|
|
167
|
+
* Whether the type an Address names is one this position accepts.
|
|
168
|
+
*
|
|
169
|
+
* The second of the two questions, and separate from the first because the
|
|
170
|
+
* accepted **set** is a property of the position while the grammar is not.
|
|
171
|
+
*
|
|
172
|
+
* **An accepted type carries its documentation form with it.** A position
|
|
173
|
+
* accepting `weapongear` accepts `docweapongear`, because the journal that
|
|
174
|
+
* documents a weapon is the same thing to a position that wanted a weapon — and
|
|
175
|
+
* a prose address of one *is* the journal, so refusing it would refuse the form
|
|
176
|
+
* an author most often writes. It is the widening `defineConfig` applies to the
|
|
177
|
+
* types a package may claim, applied here to the types a position may take.
|
|
178
|
+
*
|
|
179
|
+
* @param {AddressTuple} tuple - A parsed Address.
|
|
180
|
+
* @param {Iterable<string>} allowed - The types this position accepts.
|
|
181
|
+
* @returns {boolean} True when the Address names one of them.
|
|
182
|
+
*/
|
|
183
|
+
export function acceptsType(tuple: AddressTuple, allowed: Iterable<string>): boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Apply the defaults to a partial reading, giving a complete Address.
|
|
186
|
+
*
|
|
187
|
+
* The three rules of the module note, applied in the order they depend on each
|
|
188
|
+
* other: the package, then the system the type may fix, then the redirect the
|
|
189
|
+
* system may force.
|
|
190
|
+
*
|
|
191
|
+
* @param {{type: string, shortcode: string, package?: string, system?: string,
|
|
192
|
+
* itemDoc?: boolean}} read - A partial reading, as {@link readQualifier}
|
|
193
|
+
* returns one.
|
|
194
|
+
* @param {{package?: string, system?: string}} [where] - The citing context: the
|
|
195
|
+
* tree's own content package, and the system of the block the value sits in.
|
|
196
|
+
* @returns {AddressTuple} The complete Address.
|
|
197
|
+
*/
|
|
198
|
+
export function completeAddress(read: {
|
|
199
|
+
type: string;
|
|
200
|
+
shortcode: string;
|
|
201
|
+
package?: string;
|
|
202
|
+
system?: string;
|
|
203
|
+
itemDoc?: boolean;
|
|
204
|
+
}, where?: {
|
|
205
|
+
package?: string;
|
|
206
|
+
system?: string;
|
|
207
|
+
}): AddressTuple;
|
|
208
|
+
/**
|
|
209
|
+
* Expand a written Address to the one canonical Address it names.
|
|
210
|
+
*
|
|
211
|
+
* {@link completeAddress} and {@link renderAddress} under one name, for the
|
|
212
|
+
* index keys and cache keys that hold an Address as a string.
|
|
213
|
+
*
|
|
214
|
+
* @param {{type: string, shortcode: string, package?: string, system?: string,
|
|
215
|
+
* itemDoc?: boolean}} read - A partial reading, as {@link readQualifier}
|
|
216
|
+
* returns one.
|
|
217
|
+
* @param {{package: string, system?: string}} where - The citing context.
|
|
218
|
+
* @returns {string} The canonical `package-system-type-shortcode`.
|
|
219
|
+
*/
|
|
220
|
+
export function expandAddress(read: {
|
|
221
|
+
type: string;
|
|
222
|
+
shortcode: string;
|
|
223
|
+
package?: string;
|
|
224
|
+
system?: string;
|
|
225
|
+
itemDoc?: boolean;
|
|
226
|
+
}, where: {
|
|
227
|
+
package: string;
|
|
228
|
+
system?: string;
|
|
229
|
+
}): string;
|
|
230
|
+
/**
|
|
231
|
+
* The canonical Address of a document, from its four segments.
|
|
232
|
+
*
|
|
233
|
+
* Global uniqueness is what lets a dependency's index merge straight into a
|
|
234
|
+
* local one: the keys cannot collide by accident, so a key already present on
|
|
235
|
+
* merge is a real conflict rather than an artefact of two packages sharing a
|
|
236
|
+
* namespace. `(type, shortcode)` alone is unique only *within* a package, and
|
|
237
|
+
* two independently authored packages reaching for the same short string is a
|
|
238
|
+
* matter of time.
|
|
239
|
+
*
|
|
240
|
+
* **The system segment.** A package may ship content for more than one system,
|
|
241
|
+
* and one note then compiles into a document per system — an actor in
|
|
242
|
+
* `actors-sohl` *and* an actor in `actors-hm3`. Without a system segment both
|
|
243
|
+
* land on one key, so the address cannot name either of them.
|
|
244
|
+
*
|
|
245
|
+
* The value is a system id, or the literal **`none`** for a document no game
|
|
246
|
+
* system defines: a journal, a macro, a scene, and an item's documentation
|
|
247
|
+
* journal — which is `none` however many systems the item itself declares,
|
|
248
|
+
* because it is one journal. `none` rather than `any`, because every segment of
|
|
249
|
+
* an address is an exact literal and `any` reads as a wildcard, which a
|
|
250
|
+
* resolver written to that misreading would act on silently; and not `null` or
|
|
251
|
+
* `~`, both of which are YAML nulls that parse to an absent value and drop the
|
|
252
|
+
* segment entirely.
|
|
253
|
+
*
|
|
254
|
+
* @param {string} pkg - The owning **content** package (`sohl`, `thalorna`) —
|
|
255
|
+
* not the Foundry package, which varies per compilation target.
|
|
256
|
+
* @param {string} system - The system whose document this addresses, or `none`.
|
|
257
|
+
* @param {string} type - The note's `type`.
|
|
258
|
+
* @param {string} shortcode - The note's `shortcode`.
|
|
259
|
+
* @returns {string} `package-system-type-shortcode`, lowercased.
|
|
260
|
+
*/
|
|
261
|
+
export function canonicalKey(pkg: string, system: string, type: string, shortcode: string): string;
|
|
262
|
+
/**
|
|
263
|
+
* Reads a canonical Address back into its segments.
|
|
264
|
+
*
|
|
265
|
+
* **Nothing to read and nothing readable are different answers.** An Address
|
|
266
|
+
* that cannot be canonical — `harn-adventures-sohl-skill-melee`, five segments,
|
|
267
|
+
* because the package name carries the separator — yields `null`, while an
|
|
268
|
+
* absent or blank input yields `undefined`. Both are falsy, so every call site
|
|
269
|
+
* is unaffected; the distinction is there so a caller reporting "this address is
|
|
270
|
+
* unreadable" can tell that it has one to report about.
|
|
271
|
+
*
|
|
272
|
+
* @param {unknown} key - A canonical Address, or nothing.
|
|
273
|
+
* @returns {{package: string, system: string, type: string, shortcode: string}
|
|
274
|
+
* |null|undefined}
|
|
275
|
+
* The segments; `null` when there is a string that is not in canonical form;
|
|
276
|
+
* `undefined` when there is no Address at all.
|
|
277
|
+
*/
|
|
278
|
+
export function readCanonicalKey(key: unknown): {
|
|
279
|
+
package: string;
|
|
280
|
+
system: string;
|
|
281
|
+
type: string;
|
|
282
|
+
shortcode: string;
|
|
283
|
+
} | null | undefined;
|
|
284
|
+
/**
|
|
285
|
+
* Read a written Address as the **partial** reading, before any default applies.
|
|
286
|
+
*
|
|
287
|
+
* The first of the two questions, giving the segments the author supplied and
|
|
288
|
+
* nothing more. It exists for the callers that still hold a partial; the Address
|
|
289
|
+
* is completed by {@link completeAddress}, and **nothing an omitted segment does
|
|
290
|
+
* here is a wildcard.** Every omitted segment expands from the position's
|
|
291
|
+
* defaults, exactly as the specification's table states, so a short form names
|
|
292
|
+
* exactly one canonical Address and a lookup finds one entry or none. There is no
|
|
293
|
+
* candidate set and no ambiguity by construction.
|
|
294
|
+
*
|
|
295
|
+
* Two separators are accepted, and they are **not** interchangeable in how
|
|
296
|
+
* confidently they mark a value as qualified:
|
|
297
|
+
*
|
|
298
|
+
* - **`type-shortcode`** and its qualified forms — the canonical spelling.
|
|
299
|
+
* Obsidian reads `/` inside a wikilink as a *path* and resolves it against the
|
|
300
|
+
* vault's folders, so a slash-qualified link is a broken link in the editor
|
|
301
|
+
* where the content is authored.
|
|
302
|
+
* - **`type/shortcode`** — resolved so that a link written before the vault
|
|
303
|
+
* migrated does not silently die. A slash is *unconditionally* a qualifier:
|
|
304
|
+
* nothing else uses one, so an unknown type before it is reported rather than
|
|
305
|
+
* guessed at. The split is at the **last** slash.
|
|
306
|
+
*
|
|
307
|
+
* **Package, system and type are lowercase; the shortcode is not.** A shortcode
|
|
308
|
+
* is routinely mixed — `Clb`, `LtShoe`, `HsTunic` — and is folded rather than
|
|
309
|
+
* refused. The three segments in front of it are closed vocabularies with one
|
|
310
|
+
* spelling each, and accepting `Skill` beside `skill` would bless two ways of
|
|
311
|
+
* writing one address, so a capital there is reported. It is tested only once
|
|
312
|
+
* the value *parses*: a note name is full of capitals (`[[Shock State]]`), and
|
|
313
|
+
* calling that a badly-cased address rather than not an address would name the
|
|
314
|
+
* wrong mistake.
|
|
315
|
+
*
|
|
316
|
+
* @param {string} target - The value as authored, anchor already removed.
|
|
317
|
+
* @param {Set<string>} types - Every type the content tree contains.
|
|
318
|
+
* @param {Set<string>} [packages] - Every package an Address may name.
|
|
319
|
+
* @param {Set<string>} [noIndexPackages] - Packages declared
|
|
320
|
+
* `contentIndex: false`.
|
|
321
|
+
* @returns {{type: string, shortcode: string, itemDoc: boolean,
|
|
322
|
+
* package?: string, system?: string, reason?: undefined}
|
|
323
|
+
* | AddressProblem | null}
|
|
324
|
+
* The partial reading, carrying only the segments written; a `reason` when the
|
|
325
|
+
* value is definitely qualified but names no known type, no fetched index or a
|
|
326
|
+
* badly-cased segment; or `null` when it is not an Address at all.
|
|
327
|
+
*/
|
|
328
|
+
export function readQualifier(target: string, types: Set<string>, packages?: Set<string>, noIndexPackages?: Set<string>): {
|
|
329
|
+
type: string;
|
|
330
|
+
shortcode: string;
|
|
331
|
+
itemDoc: boolean;
|
|
332
|
+
package?: string;
|
|
333
|
+
system?: string;
|
|
334
|
+
reason?: undefined;
|
|
335
|
+
} | AddressProblem | null;
|
|
336
|
+
export { NO_SYSTEM };
|
|
337
|
+
/**
|
|
338
|
+
* The one character that separates an Address's segments.
|
|
339
|
+
*
|
|
340
|
+
* Named because it is the *grammar*, not an implementation detail: it is what a
|
|
341
|
+
* change to the address form would move, and what a reader of that change has
|
|
342
|
+
* to find.
|
|
343
|
+
*
|
|
344
|
+
* @type {string}
|
|
345
|
+
*/
|
|
346
|
+
export const ADDRESS_SEPARATOR: string;
|
|
347
|
+
/**
|
|
348
|
+
* How many segments a canonical Address has, and therefore how many
|
|
349
|
+
* {@link readCanonicalKey} counts.
|
|
350
|
+
*
|
|
351
|
+
* @type {number}
|
|
352
|
+
*/
|
|
353
|
+
export const CANONICAL_KEY_SEGMENTS: number;
|
|
354
|
+
/**
|
|
355
|
+
* The qualifier prefix that addresses a note's **documentation** rather than
|
|
356
|
+
* the note's own document: `docskill-wpnc` is the JournalEntry that
|
|
357
|
+
* `skill-wpnc`'s prose compiled into.
|
|
358
|
+
*
|
|
359
|
+
* Every doc-carrying type has a `doc<type>` counterpart, formed by prefix and
|
|
360
|
+
* never enumerated, so the prefix itself is the whole of that vocabulary.
|
|
361
|
+
*
|
|
362
|
+
* @type {string}
|
|
363
|
+
*/
|
|
364
|
+
export const ITEM_DOC_PREFIX: string;
|
|
365
|
+
/**
|
|
366
|
+
* A complete Address: four segments, and nothing else.
|
|
367
|
+
*
|
|
368
|
+
* `type` is the segment the canonical string carries, after the `doc<type>`
|
|
369
|
+
* redirect. There is no fifth field recording the written form, because the
|
|
370
|
+
* written form is a rendering rather than part of the value.
|
|
371
|
+
*/
|
|
372
|
+
export type AddressTuple = {
|
|
373
|
+
/**
|
|
374
|
+
* - The owning **content** package.
|
|
375
|
+
*/
|
|
376
|
+
package: string;
|
|
377
|
+
/**
|
|
378
|
+
* - The system whose document this addresses, or
|
|
379
|
+
* `none`.
|
|
380
|
+
*/
|
|
381
|
+
system: string;
|
|
382
|
+
/**
|
|
383
|
+
* - The type segment of the canonical Address.
|
|
384
|
+
*/
|
|
385
|
+
type: string;
|
|
386
|
+
/**
|
|
387
|
+
* - The note's `shortcode`.
|
|
388
|
+
*/
|
|
389
|
+
shortcode: string;
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* Why a written value is not an Address.
|
|
393
|
+
*
|
|
394
|
+
* The vocabulary is shared with the resolvers, so one authored mistake gets one
|
|
395
|
+
* verdict however many surfaces report it.
|
|
396
|
+
*
|
|
397
|
+
* - `not-an-address` — the value does not parse as one, which for body prose is
|
|
398
|
+
* an ordinary note name rather than a mistake.
|
|
399
|
+
* - `no-type` — the `<type>` segment is omitted and the position declares no
|
|
400
|
+
* default to fill it in, so nothing says what the value names.
|
|
401
|
+
* - `unknown-type` — the value is definitely qualified and names no known type.
|
|
402
|
+
* - `no-content-index` — the package named publishes no content index, so there
|
|
403
|
+
* is nothing to resolve the rest of the target against.
|
|
404
|
+
* - `not-lowercase` — a package, system or type segment carries a capital.
|
|
405
|
+
* - `invalid-segment` — a declared Address contains a malformed segment.
|
|
406
|
+
* - `no-package` — a declared Address cannot supply its package.
|
|
407
|
+
* - `invalid-system` — a declared Address cannot supply a registered system.
|
|
408
|
+
*/
|
|
409
|
+
export type AddressProblem = {
|
|
410
|
+
reason: string;
|
|
411
|
+
package?: string;
|
|
412
|
+
};
|
|
413
|
+
/**
|
|
414
|
+
* The vocabularies a written Address is read against.
|
|
415
|
+
*
|
|
416
|
+
* These are the tree's, not the position's — every position reads against the
|
|
417
|
+
* same ones — and they travel beside the defaults so that reading an Address is
|
|
418
|
+
* one call.
|
|
419
|
+
*/
|
|
420
|
+
export type AddressVocabulary = {
|
|
421
|
+
/**
|
|
422
|
+
* - Every type the content tree contains.
|
|
423
|
+
*/
|
|
424
|
+
types?: Set<string> | undefined;
|
|
425
|
+
/**
|
|
426
|
+
* - Every package an Address may name.
|
|
427
|
+
* Omitted by callers that resolve within one package, where the fully
|
|
428
|
+
* qualified form cannot occur.
|
|
429
|
+
*/
|
|
430
|
+
packages?: Set<string> | undefined;
|
|
431
|
+
/**
|
|
432
|
+
* - Packages declared
|
|
433
|
+
* `contentIndex: false` — a Foundry dependency only, with no fetched index. A
|
|
434
|
+
* fully qualified target naming one is refused with `no-content-index` before
|
|
435
|
+
* its type is even considered.
|
|
436
|
+
*/
|
|
437
|
+
noIndexPackages?: Set<string> | undefined;
|
|
438
|
+
};
|
|
439
|
+
/**
|
|
440
|
+
* What a position contributes: the defaults for the segments an author may omit.
|
|
441
|
+
*
|
|
442
|
+
* `package` and `system` are always supplied; `type` is supplied only by a
|
|
443
|
+
* position that accepts exactly one, and a position accepting several declares
|
|
444
|
+
* none, so a value omitting the `<type>` segment is refused rather than guessed
|
|
445
|
+
* at.
|
|
446
|
+
*/
|
|
447
|
+
export type AddressDefaults = AddressVocabulary & {
|
|
448
|
+
package?: string;
|
|
449
|
+
system?: string;
|
|
450
|
+
type?: string;
|
|
451
|
+
};
|
|
452
|
+
import { NO_SYSTEM } from "./systems.mjs";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @typedef {import("./art-slots.mjs").ArtSlot} ArtSlot */
|
|
1
2
|
/**
|
|
2
3
|
* The art slot one key names, or `undefined`.
|
|
3
4
|
*
|
|
@@ -6,13 +7,30 @@
|
|
|
6
7
|
*/
|
|
7
8
|
export function artSlot(key: unknown): ArtSlot | undefined;
|
|
8
9
|
/**
|
|
9
|
-
* The
|
|
10
|
+
* The Address an authored art value names, checked against what this position
|
|
11
|
+
* accepts.
|
|
10
12
|
*
|
|
11
|
-
* @
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* The two questions {@link module:engine/address} keeps separate: does the
|
|
14
|
+
* value parse, and is what it names acceptable *here*. {@link parseAddress}
|
|
15
|
+
* answers the first, taking the slot's own `type` as the default an omitted
|
|
16
|
+
* segment fills in; {@link acceptsType} answers the second, against the set
|
|
17
|
+
* the position declares rather than against that same default.
|
|
18
|
+
*
|
|
19
|
+
* @param {unknown} value - The value as authored.
|
|
20
|
+
* @param {string} defaultType - The type a bare value takes.
|
|
21
|
+
* @param {Iterable<string>|undefined} accepts - The types this position
|
|
22
|
+
* accepts, or `undefined` to accept whatever parses.
|
|
23
|
+
* @param {import("./address.mjs").AddressDefaults} [vocabulary] - The
|
|
24
|
+
* position's remaining defaults and the tree's vocabularies.
|
|
25
|
+
* @returns {import("./address.mjs").AddressTuple
|
|
26
|
+
* |import("./address.mjs").AddressProblem
|
|
27
|
+
* |{reason: "not-accepted", type: string}} The Address; a reason it does
|
|
28
|
+
* not parse; or `not-accepted` when it parses to a type outside `accepts`.
|
|
14
29
|
*/
|
|
15
|
-
export function artTarget(value:
|
|
30
|
+
export function artTarget(value: unknown, defaultType: string, accepts: Iterable<string> | undefined, vocabulary?: import("./address.mjs").AddressDefaults): import("./address.mjs").AddressTuple | import("./address.mjs").AddressProblem | {
|
|
31
|
+
reason: "not-accepted";
|
|
32
|
+
type: string;
|
|
33
|
+
};
|
|
16
34
|
/**
|
|
17
35
|
* The address space an asset reference resolves against, from a corpus.
|
|
18
36
|
*
|
|
@@ -60,12 +78,17 @@ export function assetAddressIndex(records?: readonly object[], { config, foreign
|
|
|
60
78
|
* or the equivalent the site and the book build.
|
|
61
79
|
* @param {unknown} value - The value as authored.
|
|
62
80
|
* @param {string} defaultType - The type a bare value takes.
|
|
81
|
+
* @param {Iterable<string>} [accepts] - The types this position accepts, for a
|
|
82
|
+
* position that declares a set narrower than "every asset type" — an art
|
|
83
|
+
* slot's. Omitted, any asset type is taken, which is an embed's rule: it
|
|
84
|
+
* draws a file, and every asset type is a file.
|
|
63
85
|
* @returns {{record: {package: string, asset: {path: string}}, pathname: string}
|
|
64
86
|
* |{record: null, reason: string, type?: string}} The asset and the pathname
|
|
65
87
|
* it is at, or a reason from
|
|
66
|
-
* {@link module:engine/wikilink-syntax.LINK_FINDING_REASONS}
|
|
88
|
+
* {@link module:engine/wikilink-syntax.LINK_FINDING_REASONS}, or `not-accepted`
|
|
89
|
+
* when `accepts` is given and the parsed type falls outside it.
|
|
67
90
|
*/
|
|
68
|
-
export function readAssetAddress(index: object, value: unknown, defaultType: string): {
|
|
91
|
+
export function readAssetAddress(index: object, value: unknown, defaultType: string, accepts?: Iterable<string>): {
|
|
69
92
|
record: {
|
|
70
93
|
package: string;
|
|
71
94
|
asset: {
|
|
@@ -84,9 +107,10 @@ export function readAssetAddress(index: object, value: unknown, defaultType: str
|
|
|
84
107
|
* @param {object} index - From {@link module:engine/wikilinks.buildWikilinkIndex}.
|
|
85
108
|
* @param {unknown} value - The value as authored.
|
|
86
109
|
* @param {string} defaultType - The type the field declares.
|
|
110
|
+
* @param {Iterable<string>} [accepts] - The types this position accepts.
|
|
87
111
|
* @returns {{package: string, asset: {path: string}}|null} The record.
|
|
88
112
|
*/
|
|
89
|
-
export function resolveArtRecord(index: object, value: unknown, defaultType: string): {
|
|
113
|
+
export function resolveArtRecord(index: object, value: unknown, defaultType: string, accepts?: Iterable<string>): {
|
|
90
114
|
package: string;
|
|
91
115
|
asset: {
|
|
92
116
|
path: string;
|
|
@@ -103,13 +127,19 @@ export function resolveArtRecord(index: object, value: unknown, defaultType: str
|
|
|
103
127
|
* @param {object} index - From {@link module:engine/wikilinks.buildWikilinkIndex}.
|
|
104
128
|
* @param {unknown} value - The value as authored.
|
|
105
129
|
* @param {string} defaultType - The type the field declares.
|
|
106
|
-
* @
|
|
107
|
-
*
|
|
108
|
-
*
|
|
130
|
+
* @param {Iterable<string>} [accepts] - The types this position accepts.
|
|
131
|
+
* @returns {{pathname: string|null, resolved: boolean, reason?: string,
|
|
132
|
+
* type?: string}} The pathname, and whether an address was actually
|
|
133
|
+
* answered — which tells a caller applying a default apart from one whose
|
|
134
|
+
* address named nothing. `reason` and `type` carry why, unresolved — in
|
|
135
|
+
* particular `not-accepted`, which the caller reports as an error rather
|
|
136
|
+
* than falling back silently.
|
|
109
137
|
*/
|
|
110
|
-
export function artPathname(index: object, value: unknown, defaultType: string): {
|
|
138
|
+
export function artPathname(index: object, value: unknown, defaultType: string, accepts?: Iterable<string>): {
|
|
111
139
|
pathname: string | null;
|
|
112
140
|
resolved: boolean;
|
|
141
|
+
reason?: string;
|
|
142
|
+
type?: string;
|
|
113
143
|
};
|
|
114
144
|
/**
|
|
115
145
|
* The default art address a being's own tags choose, or `null`.
|
|
@@ -126,34 +156,30 @@ export function beingDefaultArt(fm: object): string | null;
|
|
|
126
156
|
* What an unresolved art address is reported as.
|
|
127
157
|
*
|
|
128
158
|
* One wording, so the four compilers that can meet the case do not each invent
|
|
129
|
-
* their own.
|
|
159
|
+
* their own. A **warning**: nothing in this package or a fetched index answers
|
|
160
|
+
* the address, so the document falls back to its default art rather than
|
|
161
|
+
* shipping a path that installs nowhere.
|
|
130
162
|
*
|
|
131
163
|
* @param {string} key - The key that was authored.
|
|
132
|
-
* @param {unknown} value - The value it carried.
|
|
133
|
-
* @param {string} defaultType - The type the field declares.
|
|
164
|
+
* @param {unknown} value - The value it carried, named exactly as written.
|
|
134
165
|
* @returns {string} The message.
|
|
135
166
|
*/
|
|
136
|
-
export function unresolvedArtMessage(key: string, value: unknown
|
|
167
|
+
export function unresolvedArtMessage(key: string, value: unknown): string;
|
|
137
168
|
/**
|
|
138
|
-
*
|
|
169
|
+
* What an art address naming a type this slot does not accept is reported as.
|
|
139
170
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* @property {boolean} document - Whether the slot reaches a compiled document.
|
|
144
|
-
* @property {string} describe - One line, for the author-facing reference.
|
|
145
|
-
*/
|
|
146
|
-
/**
|
|
147
|
-
* The four art slots, in the order the specification tabulates them.
|
|
148
|
-
*
|
|
149
|
-
* `banner` is the one that reaches no compiled document: it is the page's hero
|
|
150
|
-
* image, read by the site and by the book's section plates and by nothing else.
|
|
151
|
-
* That is what `document: false` states, and it is why the inert-art check
|
|
152
|
-
* skips it — a key that is *meant* to reach no document is not an inert key.
|
|
171
|
+
* An **error**, not a warning: unlike an address nothing answers, this one
|
|
172
|
+
* names a real type, and no default art repairs an author asking for a sound
|
|
173
|
+
* where the slot takes an icon or an image.
|
|
153
174
|
*
|
|
154
|
-
* @
|
|
175
|
+
* @param {string} key - The key that was authored.
|
|
176
|
+
* @param {unknown} value - The value it carried, named exactly as written.
|
|
177
|
+
* @param {string} type - The type the address named.
|
|
178
|
+
* @param {Iterable<string>} accepts - The types this slot accepts.
|
|
179
|
+
* @returns {string} The message.
|
|
155
180
|
*/
|
|
156
|
-
export
|
|
181
|
+
export function unacceptedArtMessage(key: string, value: unknown, type: string, accepts: Iterable<string>): string;
|
|
182
|
+
export { ART_SLOTS } from "./art-slots.mjs";
|
|
157
183
|
/**
|
|
158
184
|
* The art a being falls back to, by the kind it is tagged.
|
|
159
185
|
*
|
|
@@ -169,24 +195,4 @@ export const ART_SLOTS: readonly ArtSlot[];
|
|
|
169
195
|
* @type {Readonly<Record<string, string>>}
|
|
170
196
|
*/
|
|
171
197
|
export const BEING_DEFAULT_ART: Readonly<Record<string, string>>;
|
|
172
|
-
|
|
173
|
-
* One art slot: the key a note authors, and the type a bare value takes.
|
|
174
|
-
*/
|
|
175
|
-
export type ArtSlot = {
|
|
176
|
-
/**
|
|
177
|
-
* - The key under `data:`.
|
|
178
|
-
*/
|
|
179
|
-
key: string;
|
|
180
|
-
/**
|
|
181
|
-
* - The asset type a bare shortcode defaults to.
|
|
182
|
-
*/
|
|
183
|
-
type: string;
|
|
184
|
-
/**
|
|
185
|
-
* - Whether the slot reaches a compiled document.
|
|
186
|
-
*/
|
|
187
|
-
document: boolean;
|
|
188
|
-
/**
|
|
189
|
-
* - One line, for the author-facing reference.
|
|
190
|
-
*/
|
|
191
|
-
describe: string;
|
|
192
|
-
};
|
|
198
|
+
export type ArtSlot = import("./art-slots.mjs").ArtSlot;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four art slots, in the order the specification tabulates them.
|
|
3
|
+
*
|
|
4
|
+
* `banner` is the one that reaches no compiled document: it is the page's hero
|
|
5
|
+
* image, read by the site and by the book's section plates and by nothing else.
|
|
6
|
+
* That is what `document: false` states, and it is why the inert-art check
|
|
7
|
+
* skips it — a key that is *meant* to reach no document is not an inert key.
|
|
8
|
+
*
|
|
9
|
+
* @type {readonly ArtSlot[]}
|
|
10
|
+
*/
|
|
11
|
+
export const ART_SLOTS: readonly ArtSlot[];
|
|
12
|
+
/**
|
|
13
|
+
* One art slot: the key a note authors, and the type a bare value takes.
|
|
14
|
+
*/
|
|
15
|
+
export type ArtSlot = {
|
|
16
|
+
/**
|
|
17
|
+
* - The key under `data:`.
|
|
18
|
+
*/
|
|
19
|
+
key: string;
|
|
20
|
+
/**
|
|
21
|
+
* - The asset type a bare shortcode defaults to.
|
|
22
|
+
*/
|
|
23
|
+
type: string;
|
|
24
|
+
/**
|
|
25
|
+
* - The types an authored value may
|
|
26
|
+
* name. **Not the default** — a faith tradition's profile art is a full
|
|
27
|
+
* illustration rather than a game icon, so an `icon` slot's default stays
|
|
28
|
+
* `icon` while its accepted set also takes `image`. A value naming a type
|
|
29
|
+
* outside it is an error, against this set rather than against `type`.
|
|
30
|
+
*/
|
|
31
|
+
accepts: readonly string[];
|
|
32
|
+
/**
|
|
33
|
+
* - Whether the slot reaches a compiled document.
|
|
34
|
+
*/
|
|
35
|
+
document: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* - One line, for the author-facing reference.
|
|
38
|
+
*/
|
|
39
|
+
describe: string;
|
|
40
|
+
};
|