@heroiclands/package-build 22.9.2 → 22.11.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.
Files changed (97) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/CONTENT.md +54 -0
  3. package/bin/content-build.mjs +3 -1
  4. package/docs/api.md +11 -6
  5. package/docs/commands.md +1 -1
  6. package/docs/content-format.md +411 -201
  7. package/engine/actor-compiler.mjs +20 -7
  8. package/engine/address-fields.mjs +426 -0
  9. package/engine/address-values.mjs +80 -0
  10. package/engine/address.mjs +769 -0
  11. package/engine/art-fields.mjs +112 -97
  12. package/engine/art-slots.mjs +81 -0
  13. package/engine/base-compiler.mjs +61 -7
  14. package/engine/bundle-notes.mjs +5 -2
  15. package/engine/bundles.mjs +17 -10
  16. package/engine/content-address.mjs +23 -186
  17. package/engine/content-embeds.mjs +18 -4
  18. package/engine/content-format-check.mjs +50 -22
  19. package/engine/content-index.mjs +85 -43
  20. package/engine/content-links.mjs +105 -104
  21. package/engine/content-lint.mjs +11 -1
  22. package/engine/content-tables.mjs +9 -1
  23. package/engine/field-spec.mjs +21 -4
  24. package/engine/folder-notes.mjs +44 -15
  25. package/engine/foundry-entries.mjs +3 -0
  26. package/engine/frontmatter-lint.mjs +150 -47
  27. package/engine/frontmatter.mjs +55 -10
  28. package/engine/generate.mjs +7 -1
  29. package/engine/helpers.mjs +37 -9
  30. package/engine/holdings.mjs +41 -12
  31. package/engine/index-records.mjs +2 -0
  32. package/engine/infobox-render.mjs +2 -1
  33. package/engine/infobox.mjs +26 -6
  34. package/engine/item-compiler.mjs +5 -0
  35. package/engine/item-docs.mjs +7 -44
  36. package/engine/map-dot.mjs +2 -0
  37. package/engine/map-notes.mjs +7 -2
  38. package/engine/map-places.mjs +42 -10
  39. package/engine/metadata-index.mjs +60 -5
  40. package/engine/note-addresses.mjs +325 -0
  41. package/engine/note-vocabulary.mjs +140 -28
  42. package/engine/pdf-build.mjs +1 -1
  43. package/engine/pdf-toc.mjs +17 -2
  44. package/engine/place-relations.mjs +160 -50
  45. package/engine/populations.mjs +17 -9
  46. package/engine/reference-targets.mjs +54 -0
  47. package/engine/scenes.mjs +5 -2
  48. package/engine/shortcode-references.mjs +68 -0
  49. package/engine/site-build.mjs +39 -15
  50. package/engine/site-index.mjs +57 -75
  51. package/engine/sql-tables.mjs +43 -9
  52. package/engine/system-block.mjs +21 -0
  53. package/engine/system-vocabulary.mjs +46 -0
  54. package/engine/web-wikilinks.mjs +11 -9
  55. package/engine/wikilink-syntax.mjs +21 -0
  56. package/engine/wikilinks.mjs +92 -306
  57. package/hm3/actors.mjs +8 -24
  58. package/hm3/item-fields.mjs +15 -5
  59. package/package.json +1 -1
  60. package/sohl/infobox.mjs +111 -17
  61. package/sohl/item-fields.mjs +131 -41
  62. package/types/engine/address-fields.d.mts +95 -0
  63. package/types/engine/address-values.d.mts +34 -0
  64. package/types/engine/address.d.mts +452 -0
  65. package/types/engine/art-fields.d.mts +59 -53
  66. package/types/engine/art-slots.d.mts +40 -0
  67. package/types/engine/base-compiler.d.mts +24 -2
  68. package/types/engine/bundle-notes.d.mts +2 -2
  69. package/types/engine/content-address.d.mts +3 -139
  70. package/types/engine/content-embeds.d.mts +1 -1
  71. package/types/engine/content-format-check.d.mts +25 -0
  72. package/types/engine/content-index.d.mts +2 -2
  73. package/types/engine/content-links.d.mts +6 -5
  74. package/types/engine/field-spec.d.mts +46 -9
  75. package/types/engine/folder-notes.d.mts +11 -10
  76. package/types/engine/frontmatter-lint.d.mts +7 -97
  77. package/types/engine/frontmatter.d.mts +16 -1
  78. package/types/engine/helpers.d.mts +2 -2
  79. package/types/engine/infobox.d.mts +6 -0
  80. package/types/engine/item-docs.d.mts +1 -35
  81. package/types/engine/map-notes.d.mts +2 -2
  82. package/types/engine/note-addresses.d.mts +41 -0
  83. package/types/engine/note-vocabulary.d.mts +20 -3
  84. package/types/engine/place-relations.d.mts +12 -1
  85. package/types/engine/populations.d.mts +17 -2
  86. package/types/engine/reference-targets.d.mts +9 -0
  87. package/types/engine/shortcode-references.d.mts +14 -0
  88. package/types/engine/site-build.d.mts +3 -3
  89. package/types/engine/site-index.d.mts +9 -30
  90. package/types/engine/sql-tables.d.mts +2 -2
  91. package/types/engine/system-block.d.mts +17 -0
  92. package/types/engine/system-vocabulary.d.mts +16 -0
  93. package/types/engine/wikilink-syntax.d.mts +7 -0
  94. package/types/engine/wikilinks.d.mts +6 -109
  95. package/types/hm3/item-fields.d.mts +5 -0
  96. package/types/sohl/infobox.d.mts +6 -5
  97. package/types/sohl/item-fields.d.mts +8 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * An Address position, as a field declaration states it.
3
+ *
4
+ * @typedef {object} AddressPosition
5
+ * @property {string} type - The type an omitted `<type>` segment takes.
6
+ * @property {readonly string[]} accepts - Every type the position accepts.
7
+ * @property {"value"|"items"|"keys"} [holds] - Where the Addresses are.
8
+ * Defaults to `value`.
9
+ * @property {"uuid"} [emit] - Resolve each exact target to its published Item UUID.
10
+ */
11
+ /**
12
+ * What a reduction could not do, for a caller to report.
13
+ *
14
+ * @typedef {object} AddressFinding
15
+ * @property {string} key - The field, named as an author writes it.
16
+ * @property {string} written - The authored value, so the caller can locate the
17
+ * finding at the line carrying it rather than at the key.
18
+ * @property {string} message - One sentence, naming the fix.
19
+ */
20
+ /**
21
+ * The declarations that state an Address position.
22
+ *
23
+ * @param {readonly object[]} fields - A field declaration.
24
+ * @returns {object[]} Only the fields carrying an `address` statement.
25
+ */
26
+ export function addressFields(fields: readonly object[]): object[];
27
+ /**
28
+ * Reduce every Address a compiled block holds to the Shortcode it names.
29
+ *
30
+ * The block is edited in place, as {@link
31
+ * module:engine/system-block.mergeSystemData} edits it: what the caller holds is
32
+ * the document being assembled, and a field the reduction cannot read is left
33
+ * exactly as authored — the findings stop the build, so nothing ships either
34
+ * way, and an untouched value is what a reader of the failing note sees.
35
+ *
36
+ * @param {object} emitted - The `system` block, already built.
37
+ * @param {readonly object[]} fields - The type's field declaration.
38
+ * @param {import("./address.mjs").AddressVocabulary & {package?: string}}
39
+ * vocabulary - The tree's vocabularies and the package the values are written
40
+ * in.
41
+ * @returns {AddressFinding[]} Every value that could not be reduced.
42
+ */
43
+ export function reduceAddressFields(emitted: object, fields: readonly object[], vocabulary?: import("./address.mjs").AddressVocabulary & {
44
+ package?: string;
45
+ }): AddressFinding[];
46
+ /**
47
+ * How many values a position holds.
48
+ *
49
+ * - `value` — the field's value is one Address.
50
+ * - `items` — every element of a list is one.
51
+ * - `keys` — every **key** of a map is one, and the values are untouched.
52
+ *
53
+ * @type {readonly string[]}
54
+ */
55
+ export const ADDRESS_FIELD_SHAPES: readonly string[];
56
+ /**
57
+ * An Address position, as a field declaration states it.
58
+ */
59
+ export type AddressPosition = {
60
+ /**
61
+ * - The type an omitted `<type>` segment takes.
62
+ */
63
+ type: string;
64
+ /**
65
+ * - Every type the position accepts.
66
+ */
67
+ accepts: readonly string[];
68
+ /**
69
+ * - Where the Addresses are.
70
+ * Defaults to `value`.
71
+ */
72
+ holds?: "keys" | "value" | "items" | undefined;
73
+ /**
74
+ * - Resolve each exact target to its published Item UUID.
75
+ */
76
+ emit?: "uuid" | undefined;
77
+ };
78
+ /**
79
+ * What a reduction could not do, for a caller to report.
80
+ */
81
+ export type AddressFinding = {
82
+ /**
83
+ * - The field, named as an author writes it.
84
+ */
85
+ key: string;
86
+ /**
87
+ * - The authored value, so the caller can locate the
88
+ * finding at the line carrying it rather than at the key.
89
+ */
90
+ written: string;
91
+ /**
92
+ * - One sentence, naming the fix.
93
+ */
94
+ message: string;
95
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Render typed properties at a generated-output boundary.
3
+ * @param {any} value - A tuple-bearing tree.
4
+ * @param {WeakMap<object, object>} [seen] - Shared containers in this output.
5
+ * @returns {any} A serializable tree containing full Addresses.
6
+ */
7
+ export function encodeAddresses(value: any, seen?: WeakMap<object, object>): any;
8
+ /**
9
+ * Clone mutable note state while retaining immutable Address identities.
10
+ * @param {any} value - Typed note state.
11
+ * @returns {any} An independent mutable container tree.
12
+ */
13
+ export function cloneAddressState(value: any): any;
14
+ /** Address-keyed properties hold typed entries rather than string object keys. */
15
+ export class AddressEntries {
16
+ /** @param {Array<{target: object, value: unknown, sourceKey: string}>} entries - Typed entries and diagnostic source keys. */
17
+ constructor(entries: Array<{
18
+ target: object;
19
+ value: unknown;
20
+ sourceKey: string;
21
+ }>);
22
+ entries: {
23
+ target: object;
24
+ value: unknown;
25
+ sourceKey: string;
26
+ }[];
27
+ }
28
+ /** An Address and the section within its document. */
29
+ export class AddressLink {
30
+ /** @param {object} target - Complete tuple. @param {string} anchor - Section slug. */
31
+ constructor(target: object, anchor: string);
32
+ target: object;
33
+ anchor: string;
34
+ }
@@ -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";