@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.
Files changed (100) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/CONTENT.md +33 -0
  3. package/bin/content-build.mjs +3 -1
  4. package/docs/api.md +10 -6
  5. package/docs/content-format.md +384 -200
  6. package/docs/project-setup.md +23 -0
  7. package/engine/actor-compiler.mjs +20 -7
  8. package/engine/address-fields.mjs +404 -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 +59 -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 +20 -4
  24. package/engine/folder-notes.mjs +44 -15
  25. package/engine/foundry-entries.mjs +3 -0
  26. package/engine/frontmatter-lint.mjs +144 -43
  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 +310 -0
  41. package/engine/note-vocabulary.mjs +145 -26
  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/githooks/attribution.mjs +82 -0
  58. package/githooks/commit-message.mjs +26 -0
  59. package/githooks/commit-msg +3 -35
  60. package/hm3/actors.mjs +8 -24
  61. package/hm3/item-fields.mjs +15 -5
  62. package/package.json +1 -1
  63. package/sohl/infobox.mjs +80 -17
  64. package/sohl/item-fields.mjs +114 -41
  65. package/types/engine/address-fields.d.mts +90 -0
  66. package/types/engine/address-values.d.mts +34 -0
  67. package/types/engine/address.d.mts +452 -0
  68. package/types/engine/art-fields.d.mts +59 -53
  69. package/types/engine/art-slots.d.mts +40 -0
  70. package/types/engine/base-compiler.d.mts +24 -2
  71. package/types/engine/bundle-notes.d.mts +2 -2
  72. package/types/engine/content-address.d.mts +3 -139
  73. package/types/engine/content-embeds.d.mts +1 -1
  74. package/types/engine/content-format-check.d.mts +25 -0
  75. package/types/engine/content-index.d.mts +2 -2
  76. package/types/engine/content-links.d.mts +6 -5
  77. package/types/engine/field-spec.d.mts +44 -9
  78. package/types/engine/folder-notes.d.mts +11 -10
  79. package/types/engine/frontmatter-lint.d.mts +7 -97
  80. package/types/engine/frontmatter.d.mts +16 -1
  81. package/types/engine/helpers.d.mts +2 -2
  82. package/types/engine/infobox.d.mts +6 -0
  83. package/types/engine/item-docs.d.mts +1 -35
  84. package/types/engine/map-notes.d.mts +2 -2
  85. package/types/engine/note-addresses.d.mts +41 -0
  86. package/types/engine/note-vocabulary.d.mts +20 -3
  87. package/types/engine/place-relations.d.mts +12 -1
  88. package/types/engine/populations.d.mts +17 -2
  89. package/types/engine/reference-targets.d.mts +9 -0
  90. package/types/engine/shortcode-references.d.mts +14 -0
  91. package/types/engine/site-build.d.mts +3 -3
  92. package/types/engine/site-index.d.mts +9 -30
  93. package/types/engine/sql-tables.d.mts +2 -2
  94. package/types/engine/system-block.d.mts +17 -0
  95. package/types/engine/system-vocabulary.d.mts +16 -0
  96. package/types/engine/wikilink-syntax.d.mts +7 -0
  97. package/types/engine/wikilinks.d.mts +6 -109
  98. package/types/hm3/item-fields.d.mts +5 -0
  99. package/types/sohl/infobox.d.mts +4 -5
  100. package/types/sohl/item-fields.d.mts +8 -0
@@ -1,38 +1,3 @@
1
- /**
2
- * Every content type whose **prose compiles into a JournalEntry of its own**,
3
- * addressed by the virtual `doc<type>` qualifier.
4
- *
5
- * Every item type, every actor type, every map type, plus `macro` — a macro
6
- * note's body documents the script the note also compiles into a Macro, which
7
- * is the same shape as an item and its description: one note, two documents,
8
- * the prose living in the journals pack.
9
- *
10
- * **One set, read by the compiler and the emitter alike.** The journals pass
11
- * decides what to compile from it, and the link manifest decides what to
12
- * publish a `doc<type>` entry for. Held apart, the two drift into a manifest
13
- * that asserts documentation nothing compiled — or a compiled entry no
14
- * consumer can address. It is composed exactly once, in `defineConfig`, and
15
- * read from there — never recomposed at a call site.
16
- *
17
- * `doc` notes are absent, for the reason that applies to them alone: a `doc`
18
- * note's single document *is* its prose, so there is no second document for a
19
- * `doc<type>` address to name.
20
- *
21
- * An accessor rather than a hoisted constant, so that importing this module
22
- * needs no configuration (#2).
23
- *
24
- * @returns {ReadonlySet<string>} The configured doc-carrying types.
25
- */
26
- export function docEntryTypes(): ReadonlySet<string>;
27
- /**
28
- * Whether a content note's type is one whose prose becomes a JournalEntry of
29
- * its own.
30
- *
31
- * @param {string} type - The note's `type` frontmatter.
32
- * @returns {boolean} True for an item type, an actor type, a map type and
33
- * `macro`; false for `doc`.
34
- */
35
- export function hasDocEntry(type: string): boolean;
36
1
  /**
37
2
  * The id of the JournalEntry a note's prose compiles into — an item's, or a
38
3
  * macro's.
@@ -72,3 +37,4 @@ export function itemDocEntryId(itemId: string): string;
72
37
  export function itemDocPointer(packageId: string, itemId: string, name: string, firstPageId: string): string;
73
38
  export { itemTypes };
74
39
  import { itemTypes } from "./item-registry.mjs";
40
+ export { docEntryTypes, hasDocEntry } from "./address.mjs";
@@ -161,12 +161,12 @@ export function buildScene(fm: object, ctx: object): object;
161
161
  * `bgImage` address by {@link buildScene}. Passed rather than resolved here,
162
162
  * because an address is answered by the compile's index and this function
163
163
  * takes none.
164
- * @param {(value: unknown, key: string, type: string) => string|null} [art] -
164
+ * @param {(value: unknown, key: string, type: string, accepts?: Iterable<string>) => string|null} [art] -
165
165
  * The art resolver, for the foreground overlay. Omitted, a note naming one
166
166
  * gets no overlay rather than a path nothing serves.
167
167
  * @returns {object} The Level document, keyed for the pack.
168
168
  */
169
- export function buildLevel(sohl: object, sceneId: string, img: string, art?: (value: unknown, key: string, type: string) => string | null): object;
169
+ export function buildLevel(sohl: object, sceneId: string, img: string, art?: (value: unknown, key: string, type: string, accepts?: Iterable<string>) => string | null): object;
170
170
  /**
171
171
  * Compile the `walls:` and `doors:` blocks into Wall documents.
172
172
  *
@@ -0,0 +1,41 @@
1
+ /**
2
+ * All declared Address positions of a note, including embedded notes.
3
+ * @param {object} fm - The note.
4
+ * @returns {object[]} Position descriptors with container shape and defaults.
5
+ */
6
+ export function addressPositions(fm: object, context?: {}): object[];
7
+ /**
8
+ * Decode declared properties at the note boundary. Raw mappings cannot impersonate tuples.
9
+ * @param {object} fm - Parsed YAML or an index record.
10
+ * @param {object} context - The builder's package and known types.
11
+ * @returns {object} The same note carrying typed Address properties.
12
+ */
13
+ export function decodeNoteAddresses(fm: object, context: object): object;
14
+ /**
15
+ * Read context supplied by the package configuration.
16
+ * @param {object} config - Resolved package configuration.
17
+ * @returns {object} Builder defaults and system declarations.
18
+ */
19
+ export function noteAddressContext(config: object): object;
20
+ /**
21
+ * Decode Address properties contributed by an index record.
22
+ * @param {object} record - The record.
23
+ * @param {object} context - Builder defaults.
24
+ * @returns {object} The typed record.
25
+ */
26
+ export function decodeIndexAddresses(record: object, context: object): object;
27
+ /** Structured positions whose Address is nested inside a declared container. */
28
+ export const STRUCTURED_ADDRESSES: readonly ({
29
+ path: string[];
30
+ shape: string;
31
+ type: string;
32
+ accepts: string[];
33
+ noteType?: undefined;
34
+ } | {
35
+ noteType: string;
36
+ path: string[];
37
+ shape: string;
38
+ type: string;
39
+ accepts: string[];
40
+ })[];
41
+ export { AddressEntries, encodeAddresses, cloneAddressState } from "./address-values.mjs";
@@ -279,7 +279,23 @@ export type DataFieldSpec = {
279
279
  * value — which is the honest answer wherever the specification's stated
280
280
  * shape and the shape notes are authored in today disagree.
281
281
  */
282
- kind?: "string" | "number" | "boolean" | "map" | "list" | "scalar-or-map" | undefined;
282
+ kind?: "string" | "number" | "boolean" | "map" | "list" | "scalar-or-map" | "address" | "shortcode" | undefined;
283
+ /**
284
+ * - Type of each list or pack-map value.
285
+ */
286
+ entryKind?: "address" | "shortcode" | undefined;
287
+ /**
288
+ * - Type of each map key.
289
+ */
290
+ keyKind?: "address" | "shortcode" | undefined;
291
+ /**
292
+ * - An alternative `subType:<skill-subtype>` key.
293
+ */
294
+ keySelector?: "subType" | undefined;
295
+ /**
296
+ * - Allowed Address types, separate from `ref`.
297
+ */
298
+ accepts?: readonly string[] | undefined;
283
299
  /**
284
300
  * - Human-readable shape, for a finding and for
285
301
  * documentation.
@@ -293,10 +309,11 @@ export type DataFieldSpec = {
293
309
  */
294
310
  entryShape?: string | undefined;
295
311
  /**
296
- * - The type a bare value takes. An art slot declares
312
+ * - The Address's default type, an art slot declares
297
313
  * one — `icon` for `icon` and `tokenIcon`, `image` for `bgImage` and
298
314
  * `banner` — so `icon: anvil` names `icon-anvil` while a value carrying the
299
- * separator states its own address.
315
+ * separator states its own address. Distinct from a `FieldSpec`'s `code`
316
+ * ({@link module :engine/field-spec}), which names a **Shortcode** instead.
300
317
  */
301
318
  ref?: string | undefined;
302
319
  /**
@@ -9,7 +9,7 @@ export function oppositeBearing(bearing: unknown): string | undefined;
9
9
  /**
10
10
  * Check a place note's `data.borders`.
11
11
  *
12
- * Every `to` is a shortcode a place declares, every `bearing` is one of the
12
+ * Every `to` is an Address a place declares, every `bearing` is one of the
13
13
  * eight, no pair is listed twice, no target is the note's own parent or child,
14
14
  * and the other end states the pair back at the opposite bearing — a missing
15
15
  * reciprocal is a warning naming both notes, a wrong one an error.
@@ -38,6 +38,17 @@ export function checkBorders(note: object, { index }?: {
38
38
  export function checkRoutes(note: object, { index }?: {
39
39
  index?: object | undefined;
40
40
  }): object[];
41
+ /**
42
+ * The type a border's or a route's `to` names.
43
+ *
44
+ * It is both the default its `<type>` segment takes when an author omits it and
45
+ * the whole of the set the position accepts, which is what makes `vylar` and
46
+ * `place-vylar` the same Address and a `to` naming a `lore` note an error rather
47
+ * than a lookup that quietly finds nothing.
48
+ *
49
+ * @type {string}
50
+ */
51
+ export const RELATION_TYPE: string;
41
52
  /**
42
53
  * The eight compass bearings, clockwise from north.
43
54
  *
@@ -1,3 +1,14 @@
1
+ /**
2
+ * Read a fetched index entry into a node, or `null` where it takes no part.
3
+ *
4
+ * A fetched entry carries the structure its record stated and no figure, so
5
+ * a dependency's place sits in the geography and counts nobody.
6
+ *
7
+ * @param {string} canonical - The entry's address.
8
+ * @param {object} entry - The entry.
9
+ * @returns {PopulationNode|null} The node.
10
+ */
11
+ export function foreignNode(canonical: string, entry: object): PopulationNode | null;
1
12
  /**
2
13
  * Check a place's population against the corpus around it.
3
14
  *
@@ -114,8 +125,12 @@ export type Corpus = {
114
125
  */
115
126
  holdersOf: Map<string, string[]>;
116
127
  /**
117
- * `type-shortcode` → the node,
118
- * for the citation rule.
128
+ * `type/shortcode` → the
129
+ * node, for the citation rule.
130
+ * Package-blind by design: a
131
+ * local note shadows a
132
+ * dependency's, so this is
133
+ * never a canonical Address.
119
134
  */
120
135
  byAddress: Map<string, PopulationNode>;
121
136
  };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Names and published destinations for reference presentation.
3
+ * Stubs contribute names, never pages or documents. The published index
4
+ * remains separate so body-link validation retains its publication rules.
5
+ * @param {readonly object[]} records - Decoded local or fetched records.
6
+ * @param {Map<string, object>} [published] - Actual published link destinations.
7
+ * @returns {Map<string, object>} Canonical lookup keys and presentation values.
8
+ */
9
+ export function buildReferenceTargets(records: readonly object[], published?: Map<string, object>): Map<string, object>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Present a native Shortcode using its unique system/type target.
3
+ * Address properties never use this lookup. Multiple packages claiming the
4
+ * same native identity remain ambiguous. Documentation supplies links; the
5
+ * native target supplies a name even when its documentation is unpublished.
6
+ * @param {readonly Map<string, object>[]} indexes - Published identity indexes.
7
+ * @param {unknown} code - Native Shortcode.
8
+ * @param {{type: string, system: string}} hint - Its declared native namespace.
9
+ * @returns {object|undefined} Documentation presentation, or no unique target.
10
+ */
11
+ export function resolveShortcodeReference(indexes: readonly Map<string, object>[], code: unknown, hint: {
12
+ type: string;
13
+ system: string;
14
+ }): object | undefined;
@@ -111,7 +111,7 @@ export function writeHomepages(outRoot: string, pages: readonly object[], config
111
111
  * @param {object} options.config - The resolved build configuration.
112
112
  * @returns {object} The gate results and, when they pass, the built index.
113
113
  */
114
- export function siteGates(pages: object[], findings: object, { config }: {
114
+ export function siteGates(pages: object[], findings: object, { config, records }: {
115
115
  config: object;
116
116
  }): object;
117
117
  /**
@@ -185,7 +185,7 @@ export function tableUniverse(pages: object[]): Map<string, object[]>;
185
185
  * @param {object} options
186
186
  * @param {(data: object, page: object) => void} [options.decorate] - Called
187
187
  * with each page's frontmatter, for whatever a consumer's own pass adds.
188
- * @param {(value: unknown, type: string) => string|null} [options.artSrc] -
188
+ * @param {(value: unknown, type: string, accepts?: Iterable<string>) => string|null} [options.artSrc] -
189
189
  * Translates an art address into the pathname the website resolver takes, or
190
190
  * `null` where nothing answers it.
191
191
  * @param {(src: string) => string} [options.webSrc] - Translates an authored
@@ -196,7 +196,7 @@ export function tableUniverse(pages: object[]): Map<string, object[]>;
196
196
  */
197
197
  export function pageFrontmatter(page: object, { decorate, webSrc, artSrc }: {
198
198
  decorate?: ((data: object, page: object) => void) | undefined;
199
- artSrc?: ((value: unknown, type: string) => string | null) | undefined;
199
+ artSrc?: ((value: unknown, type: string, accepts?: Iterable<string>) => string | null) | undefined;
200
200
  webSrc?: ((src: string) => string) | undefined;
201
201
  }): object;
202
202
  /**
@@ -13,7 +13,7 @@
13
13
  * ordinary dead address.
14
14
  * @returns {SiteIndex} The index, and what could not be addressed unambiguously.
15
15
  */
16
- export function buildSiteIndex(entries: readonly SiteEntry[], { foreignIndex, noIndexPackages }?: {
16
+ export function buildSiteIndex(entries: readonly SiteEntry[], { foreignIndex, noIndexPackages, package: builderPackage, records, foreignReferences, }?: {
17
17
  foreignIndex?: Map<string, {
18
18
  package: string;
19
19
  type?: string;
@@ -62,36 +62,15 @@ export function wikiContext(built: SiteIndex, { src, file, type, errors, foreign
62
62
  resolved?: object[] | undefined;
63
63
  }): object;
64
64
  /**
65
- * Resolve one infobox reference against a site index.
66
- *
67
- * A note writes a reference three ways and all three reach here: a bare
68
- * **shortcode** (`slntlncmpny`), a short **address** (`affiliation-slntlncmpny`)
69
- * and a **canonical** one (`sohl-sohl-skill-melee`). The first is the ordinary
70
- * case and the ambiguous one — a shortcode is unique within a type and not
71
- * across a tree — so a caller that knows what it expects passes `hint.type`
72
- * and the lookup is narrowed to it.
73
- *
74
- * **Without a hint the types are tried in sorted order**, so two trees holding
75
- * the same note resolve it the same way. A shortcode two types both claim
76
- * answers with the first alphabetically, which is a stable wrong answer rather
77
- * than an unstable one; a caller that cares supplies the hint.
78
- *
79
- * The `address` on the answer is the slug form — `type-shortcode` — because
80
- * that is what the book's own link map is keyed by and what a wikilink is
81
- * written as.
82
- *
83
- * @param {SiteIndex} siteIndex - The index.
84
- * @param {unknown} ref - The reference, as authored.
85
- * @param {object} [hint] - `{type}`, where the caller knows it.
86
- * @returns {{name?: string, url?: string, address?: string, subType?: string}|undefined}
87
- * The page, or `undefined` where nothing answers.
65
+ * Resolve one Address against the site's canonical identity index.
66
+ * A bare Shortcode requires a declared default type. The returned Address is
67
+ * the complete tuple; URL and display name are presentation values.
68
+ * @param {SiteIndex} siteIndex - The site index.
69
+ * @param {unknown} ref - Tuple or authored reference.
70
+ * @param {object} [hint] - A declared default type.
71
+ * @returns {object|undefined} The matching page and tuple.
88
72
  */
89
- export function resolveInfoboxRef(siteIndex: SiteIndex, ref: unknown, hint?: object): {
90
- name?: string;
91
- url?: string;
92
- address?: string;
93
- subType?: string;
94
- } | undefined;
73
+ export function resolveInfoboxRef(siteIndex: SiteIndex, ref: unknown, hint?: object): object | undefined;
95
74
  /**
96
75
  * One page the site will publish, as the index needs to see it.
97
76
  */
@@ -63,7 +63,7 @@ export function findSqlBlocks(markdown: string): Array<{
63
63
  * @returns {Promise<{query: (sql: string) => Promise<object[]>,
64
64
  * close: () => Promise<void>}>} The open database.
65
65
  */
66
- export function openNotesDatabase(records: object[], { dir, dependencies }?: {
66
+ export function openNotesDatabase(records: object[], { dir, dependencies, addressContext }?: {
67
67
  dir?: string | undefined;
68
68
  dependencies?: {
69
69
  id: string;
@@ -111,7 +111,7 @@ export function runSqlQuery(db: object, sql: string): Promise<{
111
111
  export function renderSqlTable(result: {
112
112
  columns: string[];
113
113
  rows: object[];
114
- }, { linkable, sectionLevel }?: {
114
+ }, { linkable, sectionLevel, addressContext }?: {
115
115
  linkable?: ((ref: string) => boolean) | undefined;
116
116
  sectionLevel?: number | undefined;
117
117
  }): string;
@@ -140,6 +140,23 @@ export function legacyKeyOf(field: {
140
140
  export function retiredTopLevelKey(field: {
141
141
  name?: string;
142
142
  }): string | undefined;
143
+ /**
144
+ * A shared source as the key an author writes, without the container holding it.
145
+ *
146
+ * `data.weight` is authored as `weight:` one level inside `data:`, so every
147
+ * question about the *note* rather than about the resolution order asks it under
148
+ * that spelling: which presentation overlay a row takes, which key a finding
149
+ * names, and whether two statements of one fact are the same fact. The
150
+ * specification's mapping tables write the whole path and a `data:` vocabulary
151
+ * writes the key, and this is what makes the two comparable.
152
+ *
153
+ * It takes a **name** rather than a declaration because both sides of that
154
+ * comparison reach it: one holds a field, the other a row of a table.
155
+ *
156
+ * @param {string|undefined} name - The shared source, as either side spells it.
157
+ * @returns {string} The key, without the container.
158
+ */
159
+ export function authoredKey(name: string | undefined): string;
143
160
  /**
144
161
  * Where a declared field's value came from.
145
162
  *
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Systems named by package declarations and pack targets.
3
+ * @param {object} [config] - Resolved build configuration.
4
+ * @returns {string[]} Declared system identifiers.
5
+ */
6
+ export function declaredSystems(config?: object): string[];
7
+ /**
8
+ * System field vocabularies supplied by the configuration and caller.
9
+ * @param {object} [config] - Resolved build configuration.
10
+ * @param {object} [options] - Caller schema context.
11
+ * @param {string} [options.schemaSystem] - Owner of the caller's schemas.
12
+ * @returns {object} System block declarations.
13
+ */
14
+ export function systemBlocksFor(config?: object, { schemaSystem }?: {
15
+ schemaSystem?: string | undefined;
16
+ }): object;
@@ -167,6 +167,13 @@ export function linkFindingMessage({ reason, target, packages, anchor, type, stu
167
167
  * @returns {boolean} True when the link is same-page.
168
168
  */
169
169
  export function isSamePage({ target, anchor }: ParsedWikilink): boolean;
170
+ /**
171
+ * Read an operational link target in its enclosing Address context.
172
+ * @param {string} inner - Lexical link contents.
173
+ * @param {object} context - Builder defaults and type vocabulary.
174
+ * @returns {object} Typed target and diagnostic source spelling.
175
+ */
176
+ export function readWikilink(inner: string, context: object): object;
170
177
  /**
171
178
  * What a `[[…]]` **is**, before anything decides where it points.
172
179
  *
@@ -1,108 +1,3 @@
1
- /**
2
- * Reads a qualifier as the **virtual `doc<type>`** form, or reports that it is
3
- * not one.
4
- *
5
- * A document and its documentation are two documents in two packs, so they
6
- * need two addresses. `skill/wpnc` is the item; `docskill/wpnc` is the
7
- * JournalEntry its prose compiled into, and `docmacro/autoattack` is the same
8
- * arrangement for a macro.
9
- *
10
- * The virtual form exists for a type that carries separate documentation
11
- * ({@link sohl.utils.packs.docEntryTypes} — the set the journals compiler and
12
- * the link manifest read too), **or** for one that routes to the items pack.
13
- * The second clause is the older rule and stays: types that compile into items
14
- * are the open, unenumerated set, and a foreign package may publish an
15
- * item type this build has never heard of. Dropping it would silently unlink
16
- * every `doc<type>` address into such a package.
17
- *
18
- * A **real** type of the same name always wins: the virtual reading is only
19
- * consulted for a qualifier no authored note claims.
20
- *
21
- * @param {string} qualifier - The already-normalised text before the `/`.
22
- * @param {Set<string>} types - Every type the content tree contains.
23
- * @returns {string|null} The underlying document type, or `null` when the
24
- * qualifier is not a virtual one.
25
- */
26
- export function resolveItemDocType(qualifier: string, types: Set<string>): string | null;
27
- /**
28
- * Read a link target as a **qualified** `type-shortcode` reference, or report
29
- * that it does not parse as one.
30
- *
31
- * Two separators are accepted, and they are **not** interchangeable in how
32
- * confidently they mark a target as qualified:
33
- *
34
- * - **`type-shortcode`** and its qualified forms — the canonical spelling.
35
- * Obsidian reads `/` inside a wikilink as a *path* and resolves it
36
- * against the vault's folders, so a slash-qualified link is a broken link in
37
- * the editor where the content is now authored.
38
- * - **`type/shortcode`** — the legacy form, still resolved so that a link
39
- * written before the vault migrated does not silently die. A slash is
40
- * *unconditionally* a qualifier: nothing else uses one, so an unknown type
41
- * before it is reported rather than guessed at. The split is at the **last**
42
- * slash, as it always was.
43
- *
44
- * **The grammar is strict, and omission runs left to right**:
45
- *
46
- * ```text
47
- * [[[[<package>-]<system>-]<type>-]<shortcode>]
48
- * ```
49
- *
50
- * So the written forms are exactly the suffixes of the canonical address —
51
- * `type-shortcode`, `system-type-shortcode`, `package-system-type-shortcode` —
52
- * and **`package-type-shortcode` is not one of them**. A link into another
53
- * package must therefore be fully qualified, which is the price of the segment
54
- * being positional rather than tagged.
55
- *
56
- * **Parsing is plain positional counting**, the same rule
57
- * {@link readCanonicalKey} follows, and it is sound for the same reason: every
58
- * segment matches `ADDRESS_SEGMENT_PATTERN` (enforced on shortcodes by
59
- * `content-lint.mjs`), so the hyphen is purely a
60
- * separator and the count alone determines every field. Verified across the
61
- * four content trees: 138,204 authored shortcodes, none carrying a separator.
62
- *
63
- * That replaced a first-hyphen split which let a shortcode contain a hyphen
64
- * (`trauma-self-pro` → `trauma` + `self-pro`). The tolerance predates the
65
- * charset rule and
66
- * outlived it; no tree has used it, and keeping it would make a three-segment
67
- * target ambiguous between a system and a hyphenated shortcode.
68
- *
69
- * **`sohl` is both a package and a system**, and positional counting is what
70
- * makes that harmless: three segments is `<system>-<type>-<shortcode>` whatever
71
- * the first segment could also have named, and four is the full form. Nothing
72
- * has to guess which sense was meant.
73
- *
74
- * **A partial address states what it states, and the rest is not invented.** An
75
- * omitted **system** is a *wildcard* — most links target items, which belong to
76
- * a system — so the caller matches on the segments supplied and requires
77
- * exactly one hit. An omitted **package** is instead *defaulted* to the citing
78
- * note's own, so an unqualified link resolves locally and only locally.
79
- *
80
- * @param {string} target - The link target, anchor already removed.
81
- * @param {Set<string>} types - Every type the content tree contains.
82
- * @param {Set<string>} [packages] - Every package an address may name. Omitted
83
- * by callers that resolve within one package, where the form cannot occur.
84
- * @param {Set<string>} [noIndexPackages] - Packages declared `contentIndex:
85
- * false` — a Foundry dependency only, with no fetched index. A fully
86
- * qualified target naming one is refused with `no-content-index` before its
87
- * type is even considered, since there is no index to resolve it against.
88
- * @returns {{type: string, shortcode: string, itemDoc: boolean,
89
- * package?: string, system?: string, reason?: undefined}
90
- * | {reason: "unknown-type"|"no-content-index", package?: string} | null}
91
- * The resolved qualifier; a `reason` when the target is definitely qualified
92
- * but names no known type or no fetched index; or `null` when it is not an
93
- * address at all.
94
- */
95
- export function readQualifier(target: string, types: Set<string>, packages?: Set<string>, noIndexPackages?: Set<string>): {
96
- type: string;
97
- shortcode: string;
98
- itemDoc: boolean;
99
- package?: string;
100
- system?: string;
101
- reason?: undefined;
102
- } | {
103
- reason: "unknown-type" | "no-content-index";
104
- package?: string;
105
- } | null;
106
1
  /**
107
2
  * The deterministic JournalEntryPage id for one anchor: SHA-256 of
108
3
  * `"<noteId>-<anchorSlug>"`, base64-encoded, reduced to the 16 alphanumeric
@@ -157,7 +52,7 @@ export function buildWikilinkIndex(docs: Array<{
157
52
  docPack?: string;
158
53
  none?: boolean;
159
54
  draft?: boolean;
160
- }>, packageId: string, foreign?: Map<string, object>, contentPackage?: string, { assets, noIndexPackages }?: {
55
+ }>, packageId: string, foreign?: Map<string, object>, contentPackage?: string, { assets, noIndexPackages, referenceTargets, foreignReferences }?: {
161
56
  assets?: Map<string, object> | undefined;
162
57
  noIndexPackages?: Set<string> | undefined;
163
58
  }): {
@@ -232,16 +127,18 @@ export function convertWikilinks(markdown: string, { type, id, pack, docPack, in
232
127
  * @param {object} index - From {@link buildWikilinkIndex}.
233
128
  * @param {unknown} ref - The reference, as authored.
234
129
  * @param {object} [hint] - `{type}`, where the caller knows what it expects.
235
- * @returns {{name?: string, uuid?: string, address?: string, subType?: string}|undefined}
130
+ * @returns {{name?: string, uuid?: string, address?: import("./address.mjs").AddressTuple, subType?: string}|undefined}
236
131
  * The target, or `undefined` where nothing answers.
237
132
  */
238
133
  export function resolveReference(index: object, ref: unknown, hint?: object): {
239
134
  name?: string;
240
135
  uuid?: string;
241
- address?: string;
136
+ address?: import("./address.mjs").AddressTuple;
242
137
  subType?: string;
243
138
  } | undefined;
244
139
  import { ITEM_PACK } from "./ids.mjs";
245
140
  import { PACK_BY_TYPE } from "./ids.mjs";
246
141
  import { packForType } from "./ids.mjs";
247
- export { ITEM_PACK, PACK_BY_TYPE, packForType };
142
+ import { readQualifier } from "./address.mjs";
143
+ import { resolveItemDocType } from "./address.mjs";
144
+ export { ITEM_PACK, PACK_BY_TYPE, packForType, readQualifier, resolveItemDocType };
@@ -7,6 +7,11 @@
7
7
  * a registry is addressed by what a note calls itself. What the document is
8
8
  * called is the map's answer, and only the map's.
9
9
  *
10
+ * Each field names both of its authored positions, as
11
+ * {@link module:hm3/actors} does and for the same reason: `name` is the shared
12
+ * source the content format's mapping tables state, and `legacyKey` is the key
13
+ * the `hm3:` block still carries.
14
+ *
10
15
  * @type {Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>}
11
16
  */
12
17
  export const HM3_ITEM_FIELDS: Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>;
@@ -2,11 +2,10 @@
2
2
  * What one entry of `sohl.items` names.
3
3
  *
4
4
  * An entry addresses its item three ways and every tree uses all three: a
5
- * `model` address whose last two hyphen-separated segments are always
6
- * `<type>-<shortcode>`, explicit `type` / `shortcode` keys, or a locally
7
- * authored item stating its type with the shortcode inside its own `system`
8
- * block. One decode covers all of them, so nothing downstream has to know
9
- * which form a note happened to use.
5
+ * `model` address, explicit `type` / `shortcode` keys, or a locally authored
6
+ * item stating its type with the shortcode inside its own `system` block. One
7
+ * decode covers all of them, so nothing downstream has to know which form a
8
+ * note happened to use.
10
9
  *
11
10
  * @param {object} entry - One `sohl.items` entry.
12
11
  * @returns {{type: string, shortcode: string, name: string, system: object}|undefined}
@@ -2,6 +2,14 @@
2
2
  * Every item type's frontmatter vocabulary, in the order the `system` block
3
3
  * emits it.
4
4
  *
5
+ * **A field whose shared source is a path into `data:` names both positions.**
6
+ * `name` is the source the content format's mapping tables state — `data.seat`,
7
+ * `data.weight` — and `legacyKey` is the key the system block still carries, which
8
+ * wins while a tree writes it. Naming only the source would move the in-block
9
+ * read to a key spelled `data.seat`, which nothing authors; naming only the key
10
+ * leaves the container the specification points at unread, and the field then
11
+ * compiles its default over an authored value with nothing said.
12
+ *
5
13
  * @type {Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>}
6
14
  */
7
15
  export const ITEM_FIELDS: Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>;