@heroiclands/package-build 17.2.0 → 18.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/CHANGELOG.md +1711 -0
  2. package/CONTENT.md +288 -72
  3. package/README.md +1 -1
  4. package/bin/content-build.mjs +218 -137
  5. package/bin/package-build.mjs +43 -0
  6. package/content-config.mjs +62 -67
  7. package/docs/content-format.md +768 -58
  8. package/engine/actor-compiler.mjs +586 -0
  9. package/engine/address-charset.mjs +11 -8
  10. package/engine/address-diff.mjs +266 -22
  11. package/engine/anchored-sections.mjs +83 -0
  12. package/engine/anchors.mjs +83 -0
  13. package/engine/base-compiler.mjs +148 -18
  14. package/engine/bundle-notes.mjs +276 -0
  15. package/engine/bundles.mjs +307 -0
  16. package/engine/code-fences.mjs +103 -0
  17. package/engine/compile-corpus.mjs +89 -0
  18. package/engine/content-address.mjs +267 -5
  19. package/engine/content-format-check.mjs +54 -11
  20. package/engine/content-format.mjs +37 -5
  21. package/engine/content-index.mjs +225 -127
  22. package/engine/content-links.mjs +185 -77
  23. package/engine/content-lint.mjs +232 -14
  24. package/engine/content-package.mjs +2 -1
  25. package/engine/content-tables.mjs +99 -3
  26. package/engine/document-subtypes.mjs +49 -6
  27. package/engine/field-reference.mjs +1 -1
  28. package/engine/field-spec.mjs +56 -6
  29. package/engine/folder-notes.mjs +470 -0
  30. package/engine/foreign-catalog.mjs +195 -4
  31. package/engine/{manifest-emit.mjs → foundry-entries.mjs} +55 -83
  32. package/engine/frontmatter-lint.mjs +245 -19
  33. package/engine/frontmatter.mjs +88 -8
  34. package/engine/generate.mjs +304 -13
  35. package/engine/helpers.mjs +293 -64
  36. package/engine/ids.mjs +137 -2
  37. package/engine/index-records.mjs +126 -0
  38. package/engine/index.mjs +23 -5
  39. package/engine/item-compiler.mjs +349 -0
  40. package/engine/item-docs.mjs +7 -1
  41. package/engine/item-registry.mjs +6 -0
  42. package/engine/journals.mjs +93 -27
  43. package/engine/macros.mjs +4 -2
  44. package/engine/metadata-index.mjs +495 -0
  45. package/engine/note-claims.mjs +146 -25
  46. package/engine/note-ids.mjs +112 -0
  47. package/engine/note-renames.mjs +134 -0
  48. package/engine/note-schemas.mjs +25 -0
  49. package/engine/note-vocabulary.mjs +110 -27
  50. package/engine/pack-config.mjs +4 -0
  51. package/engine/pack-router.mjs +23 -0
  52. package/engine/retired-fields.mjs +137 -3
  53. package/engine/scenes.mjs +66 -34
  54. package/engine/schema-check.mjs +58 -17
  55. package/engine/site-build.mjs +71 -12
  56. package/engine/site-index.mjs +20 -7
  57. package/engine/sql-tables.mjs +485 -0
  58. package/engine/subtype-registry.mjs +102 -0
  59. package/engine/system-block.mjs +96 -12
  60. package/engine/systems.mjs +201 -0
  61. package/engine/web-wikilinks.mjs +50 -20
  62. package/engine/wikilink-syntax.mjs +17 -8
  63. package/engine/wikilinks.mjs +124 -51
  64. package/engine/yaml-lint.mjs +282 -0
  65. package/hm3/actors.mjs +352 -0
  66. package/hm3/default-item-art.mjs +75 -0
  67. package/hm3/document-subtypes.mjs +134 -0
  68. package/hm3/index.mjs +56 -0
  69. package/hm3/item-builders.mjs +84 -0
  70. package/hm3/item-fields.mjs +180 -0
  71. package/hm3/items.mjs +96 -0
  72. package/hm3/template-priority.mjs +85 -0
  73. package/manifest.mjs +43 -2
  74. package/package.json +18 -4
  75. package/release.mjs +62 -7
  76. package/sohl/actors.mjs +33 -487
  77. package/sohl/being-info.mjs +16 -7
  78. package/sohl/default-item-art.mjs +14 -3
  79. package/sohl/document-subtypes.mjs +16 -10
  80. package/sohl/item-builders.mjs +14 -5
  81. package/sohl/item-fields.mjs +68 -7
  82. package/sohl/items.mjs +44 -258
  83. package/sohl/note-schemas.mjs +2 -2
  84. package/types/content-config.d.mts +21 -42
  85. package/types/engine/actor-compiler.d.mts +204 -0
  86. package/types/engine/address-charset.d.mts +11 -8
  87. package/types/engine/address-diff.d.mts +53 -5
  88. package/types/engine/anchored-sections.d.mts +21 -0
  89. package/types/engine/anchors.d.mts +20 -0
  90. package/types/engine/base-compiler.d.mts +17 -17
  91. package/types/engine/bundle-notes.d.mts +173 -0
  92. package/types/engine/bundles.d.mts +60 -0
  93. package/types/engine/code-fences.d.mts +43 -0
  94. package/types/engine/compile-corpus.d.mts +32 -0
  95. package/types/engine/content-address.d.mts +205 -5
  96. package/types/engine/content-format-check.d.mts +6 -2
  97. package/types/engine/content-format.d.mts +57 -1
  98. package/types/engine/content-index.d.mts +54 -49
  99. package/types/engine/content-links.d.mts +52 -7
  100. package/types/engine/content-lint.d.mts +10 -1
  101. package/types/engine/content-package.d.mts +2 -1
  102. package/types/engine/content-tables.d.mts +21 -39
  103. package/types/engine/document-subtypes.d.mts +37 -3
  104. package/types/engine/field-spec.d.mts +76 -5
  105. package/types/engine/folder-notes.d.mts +159 -0
  106. package/types/engine/foreign-catalog.d.mts +53 -0
  107. package/types/engine/{manifest-emit.d.mts → foundry-entries.d.mts} +2 -43
  108. package/types/engine/frontmatter-lint.d.mts +10 -2
  109. package/types/engine/frontmatter.d.mts +64 -0
  110. package/types/engine/generate.d.mts +38 -0
  111. package/types/engine/helpers.d.mts +94 -30
  112. package/types/engine/ids.d.mts +96 -0
  113. package/types/engine/index-records.d.mts +68 -0
  114. package/types/engine/index.d.mts +9 -3
  115. package/types/engine/item-compiler.d.mts +131 -0
  116. package/types/engine/journals.d.mts +47 -9
  117. package/types/engine/metadata-index.d.mts +226 -0
  118. package/types/engine/note-claims.d.mts +57 -11
  119. package/types/engine/note-ids.d.mts +38 -0
  120. package/types/engine/note-renames.d.mts +102 -0
  121. package/types/engine/note-vocabulary.d.mts +44 -8
  122. package/types/engine/retired-fields.d.mts +75 -0
  123. package/types/engine/scenes.d.mts +3 -2
  124. package/types/engine/schema-check.d.mts +25 -4
  125. package/types/engine/site-build.d.mts +4 -4
  126. package/types/engine/site-index.d.mts +1 -1
  127. package/types/engine/sql-tables.d.mts +185 -0
  128. package/types/engine/subtype-registry.d.mts +49 -0
  129. package/types/engine/system-block.d.mts +40 -1
  130. package/types/engine/systems.d.mts +106 -0
  131. package/types/engine/web-wikilinks.d.mts +4 -2
  132. package/types/engine/wikilink-syntax.d.mts +10 -3
  133. package/types/engine/wikilinks.d.mts +41 -13
  134. package/types/engine/yaml-lint.d.mts +107 -0
  135. package/types/hm3/actors.d.mts +48 -0
  136. package/types/hm3/default-item-art.d.mts +42 -0
  137. package/types/hm3/document-subtypes.d.mts +24 -0
  138. package/types/hm3/index.d.mts +7 -0
  139. package/types/hm3/item-builders.d.mts +11 -0
  140. package/types/hm3/item-fields.d.mts +12 -0
  141. package/types/hm3/items.d.mts +23 -0
  142. package/types/hm3/template-priority.d.mts +21 -0
  143. package/types/manifest.d.mts +22 -1
  144. package/types/release.d.mts +9 -4
  145. package/types/sohl/actors.d.mts +7 -74
  146. package/types/sohl/being-info.d.mts +8 -27
  147. package/types/sohl/default-item-art.d.mts +5 -3
  148. package/types/sohl/items.d.mts +17 -32
  149. package/engine/foreign-manifests.mjs +0 -126
  150. package/engine/kb-manifest.mjs +0 -490
  151. package/types/engine/foreign-manifests.d.mts +0 -43
  152. package/types/engine/kb-manifest.d.mts +0 -241
@@ -2,7 +2,7 @@
2
2
  * Whether this package publishes the pages its content tree compiles to.
3
3
  *
4
4
  * The one question every reader of the mode actually asks — the site build, to
5
- * decide whether to walk the tree at all, and the link-manifest emitter, to
5
+ * decide whether to walk the tree at all, and the content index, to
6
6
  * decide whether an entry carries a web `path`. Written once here so the two
7
7
  * cannot come to disagree about what a mode means.
8
8
  *
@@ -39,13 +39,12 @@ export function defineConfig(config: ContentBuildConfigInput): ContentBuildConfi
39
39
  export const PACKAGE_KINDS: readonly ["systems", "modules"];
40
40
  export namespace DEFAULT_PATHS {
41
41
  let content: "assets/content";
42
- let manifests: "assets/manifests";
43
- let manifestOut: "build/manifests";
44
42
  let contentIndex: "build/content-index";
45
43
  let packJson: "build/packs-json";
46
44
  let stage: "build/stage/packs";
47
45
  let unpack: "build/tmp/packs";
48
46
  let foreignCache: "build/cache/foreign";
47
+ let metadataCache: "build/cache/metadata";
49
48
  }
50
49
  /**
51
50
  * The Foundry document types a compendium pack may hold. This is the set the
@@ -89,7 +88,7 @@ export const RETIRED_ADDRESS_KEYS: Readonly<Record<string, string>>;
89
88
  * `sohl`, whose knowledgebase is one surface among several, and empty for
90
89
  * `thalorna`, whose site is nothing but its content. It is not the package's
91
90
  * own mount point: where the package itself is served is the consuming build's
92
- * knowledge, held in `PACKAGE_BASE` (`engine/kb-manifest.mjs`) and prefixed at
91
+ * knowledge, held in `PACKAGE_BASE` (`engine/content-address.mjs`) and prefixed at
93
92
  * resolve time, so it is never recorded here (#1465).
94
93
  *
95
94
  * It is the whole scheme: `landing`, the key that named which note addressed a
@@ -110,7 +109,7 @@ export const DEFAULT_ADDRESS_SCHEME: Readonly<{
110
109
  *
111
110
  * - `homepage` — the authored homepage, and **no other page**. The content tree
112
111
  * is not walked for pages, `site.sections` / `site.trees` / `site.landing`
113
- * emit nothing, and link-manifest entries carry no web `path`.
112
+ * emit nothing, and nothing serves a page for its addresses.
114
113
  * - `content` — the homepage *plus* every page the content tree publishes: the
115
114
  * knowledgebase, the extra trees, the section landings.
116
115
  *
@@ -163,7 +162,7 @@ export const DERIVED_SYSTEM_VERSION: symbol;
163
162
  *
164
163
  * - `homepage` — the authored homepage, and **no other page**. The content tree
165
164
  * is not walked for pages, `site.sections` / `site.trees` / `site.landing`
166
- * emit nothing, and link-manifest entries carry no web `path`.
165
+ * emit nothing, and nothing serves a page for its addresses.
167
166
  * - `content` — the homepage *plus* every page the content tree publishes: the
168
167
  * knowledgebase, the extra trees, the section landings.
169
168
  *
@@ -284,19 +283,6 @@ export type PathsInput = {
284
283
  * Content tree root.
285
284
  */
286
285
  content?: string | undefined;
287
- /**
288
- * Vendored cross-package link manifests,
289
- * read by `links`. Inbound.
290
- */
291
- manifests?: string | undefined;
292
- /**
293
- * Where `manifest` writes this package's
294
- * own link manifest. Outbound, and a
295
- * build artifact — the published copy is
296
- * the one a consumer vendors into its
297
- * `manifests` directory.
298
- */
299
- manifestOut?: string | undefined;
300
286
  /**
301
287
  * Where `content-index` writes this
302
288
  * package's note index. Outbound, and a
@@ -316,18 +302,32 @@ export type PathsInput = {
316
302
  * Where `unpack` extracts JSON back to.
317
303
  */
318
304
  unpack?: string | undefined;
305
+ /**
306
+ * Where a dependency declaring
307
+ * `itemCatalog: true` is unpacked.
308
+ * Inbound, and fetched rather than
309
+ * committed.
310
+ */
311
+ foreignCache?: string | undefined;
312
+ /**
313
+ * Where a dependency's published content
314
+ * index is fetched to (#239). Inbound,
315
+ * for *every* declared dependency, not
316
+ * only those supplying a catalogue.
317
+ */
318
+ metadataCache?: string | undefined;
319
319
  };
320
320
  /**
321
321
  * {@link PathsInput}, resolved to absolute paths against `rootDir`.
322
322
  */
323
323
  export type ResolvedPaths = {
324
324
  content: string;
325
- manifests: string;
326
- manifestOut: string;
327
325
  contentIndex: string;
328
326
  packJson: string;
329
327
  stage: string;
330
328
  unpack: string;
329
+ foreignCache: string;
330
+ metadataCache: string;
331
331
  };
332
332
  /**
333
333
  * The identity every compiled document's `_stats` block carries.
@@ -371,32 +371,12 @@ export type StatsSpec = {
371
371
  * two places for one fact.
372
372
  */
373
373
  export type PackageBuildSection = Record<string, unknown>;
374
- /**
375
- * The two manifest switches. A package may publish a link manifest, consume
376
- * other packages' manifests, both, or neither — the four combinations are all
377
- * real (see #1385/#1446: `kethira` consumes but never publishes).
378
- */
379
- export type ManifestSwitches = {
380
- /**
381
- * Emit this package's link manifest.
382
- */
383
- publish: boolean;
384
- /**
385
- * Resolve cross-package links through vendored manifests.
386
- */
387
- consume: boolean;
388
- };
389
374
  export type PublishSwitches = {
390
375
  /**
391
376
  * How much of this package reaches the web.
392
377
  * See {@link SITE_MODES}.
393
378
  */
394
379
  site: SiteMode;
395
- manifests: ManifestSwitches;
396
- };
397
- export type ManifestSwitchesInput = {
398
- publish?: boolean | undefined;
399
- consume?: boolean | undefined;
400
380
  };
401
381
  /**
402
382
  * The **Foundry core** version range this package supports.
@@ -512,7 +492,6 @@ export type DocsSpec = {
512
492
  };
513
493
  export type PublishSwitchesInput = {
514
494
  site?: SiteMode | undefined;
515
- manifests?: ManifestSwitchesInput | undefined;
516
495
  address?: AddressSchemeInput | undefined;
517
496
  };
518
497
  export type AddressSchemeInput = {
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Strip compendium-only fields from a predefined item before embedding it
3
+ * inside an actor's `items[]`. These fields belong on a top-level
4
+ * compendium document, not on an embedded one.
5
+ *
6
+ * @param {object} item - The catalogue entry.
7
+ * @returns {object} The entry, without its compendium-only fields.
8
+ */
9
+ export function stripCompendiumFields(item: object): object;
10
+ /**
11
+ * Whether a value is a plain object, for {@link deepMerge}.
12
+ *
13
+ * @param {unknown} v - The value.
14
+ * @returns {boolean} True for a plain object.
15
+ */
16
+ export function isPlainObject(v: unknown): boolean;
17
+ /**
18
+ * Recursively merge `overlay` onto `base`. Plain objects merge key-by-key;
19
+ * everything else (arrays, primitives, null) replaces. Inputs are not
20
+ * mutated.
21
+ *
22
+ * @param {any} base - The catalogue entry.
23
+ * @param {any} overlay - The note's own values.
24
+ * @returns {any} The merged value.
25
+ */
26
+ export function deepMerge(base: any, overlay: any): any;
27
+ /**
28
+ * The key one predefined item is held under, and every place that spells it.
29
+ *
30
+ * **The vocabulary is the document's, not the note's** — `subType` is the
31
+ * Foundry Item subtype the compiled document carries, which is the only thing a
32
+ * compiled pack (or an extracted dependency catalogue) records about what an
33
+ * item *is*. An actor's frontmatter addresses the same item in the *note*
34
+ * vocabulary, so a reference is translated forward through the system's map
35
+ * before it reaches this function; see
36
+ * {@link SystemActorCompiler#embeddedSubtype} for why the translation goes that
37
+ * way and not the other (#140).
38
+ *
39
+ * @param {string} subType - The Foundry Item subtype.
40
+ * @param {string} shortcode - The item's `system.shortcode`.
41
+ * @returns {string} The address, `subType:shortcode`.
42
+ */
43
+ export function itemAddress(subType: string, shortcode: string): string;
44
+ /**
45
+ * What identifies one embedded item on its actor.
46
+ *
47
+ * **Its own `system.shortcode`** — not the entry's top-level `shortcode`, which
48
+ * merely *selects* the catalogue template the entry is written from and is
49
+ * never written to the document. Two daggers may share a selector; they are two
50
+ * embodiments and each must declare its own.
51
+ *
52
+ * The name is a last resort, for a **stand-alone** entry that names no template
53
+ * and states no shortcode. It is a poor identity — presentation, and free to be
54
+ * localized or to diverge — so it is not a fallback the compiler is content
55
+ * with: two entries reducing to one identity is refused either way, and the
56
+ * message says to state a `system.shortcode`.
57
+ *
58
+ * @param {object} item - The merged embedded item.
59
+ * @returns {string} The identity, for {@link embeddedItemId}.
60
+ */
61
+ export function embeddedIdentity(item: object): string;
62
+ /**
63
+ * The `_id` of one item embedded on an actor.
64
+ *
65
+ * Seeded by the owning actor, because an embedded id must be unique within its
66
+ * **parent document** and nothing wider — so the namespace is already exactly
67
+ * the scope the uniqueness is required in, and the largest namespace in the
68
+ * four corpora holds 180 items, at which 64 bits collide with probability
69
+ * around 10⁻¹⁵.
70
+ *
71
+ * **It takes no index** (#268). Keying on a position meant reordering a being's
72
+ * item list renumbered every id after the change, so a re-import created new
73
+ * documents beside the old ones — while nothing about those documents had
74
+ * changed, only their neighbours. The identity always exists or must be stated;
75
+ * see {@link embeddedIdentity}.
76
+ *
77
+ * Keyed by the **document subtype**, so renaming a note type (#78) leaves every
78
+ * embedded id where it was.
79
+ *
80
+ * @param {string} actorId - The owning actor's id.
81
+ * @param {string} subType - The Foundry Item subtype.
82
+ * @param {string} identity - From {@link embeddedIdentity}.
83
+ * @returns {string} A 16-character Foundry id.
84
+ */
85
+ export function embeddedItemId(actorId: string, subType: string, identity: string): string;
86
+ /**
87
+ * Load every JSON file under each of `itemsSourceDirs`, returning one Map keyed
88
+ * by {@link itemAddress} — the compiled document's **subtype** and its
89
+ * `system.shortcode`. Folder docs and entries without a shortcode are skipped.
90
+ * The `_key` field is stripped from each entry — it is not part of the item
91
+ * data model.
92
+ *
93
+ * The directories are read as one address space, because an actor names an item
94
+ * by `(type, shortcode)` and never by the pack it happens to ship in. Two local
95
+ * Item packs claiming the same address is therefore ambiguous rather than a
96
+ * last-one-wins ordering detail, and fails here.
97
+ *
98
+ * A **foreign** directory — the extracted item catalogue of a package this
99
+ * repository depends on but does not contain — is a fallback rather than a
100
+ * peer. A repository must be able to ship its own `skill:awar` that stands in
101
+ * front of the system's, so a local address shadows a foreign one instead of
102
+ * colliding with it. Local directories are therefore read first, and anything
103
+ * already claimed is left alone.
104
+ *
105
+ * @param {readonly string[]} itemsSourceDirs - Every local Item pack's JSON tree.
106
+ * @param {readonly string[]} [foreignSourceDirs] - Extracted dependency
107
+ * catalogues, consulted only for addresses no local pack defines.
108
+ * @returns {Map<string, object>} The predefined items, by address.
109
+ */
110
+ export function loadItemsMap(itemsSourceDirs: readonly string[], foreignSourceDirs?: readonly string[]): Map<string, object>;
111
+ /**
112
+ * The Actor compile pass of one game system.
113
+ *
114
+ * A subclass declares its {@link SystemActorCompiler.documentSubtypes} and
115
+ * implements `buildEntry`, which is where the system's own data model is: what
116
+ * an actor's `system` block holds, which art it defaults to, which frontmatter
117
+ * becomes embedded items. Nothing above that line is a system's business.
118
+ */
119
+ export class SystemActorCompiler extends BasePackCompiler {
120
+ /**
121
+ * The note-type → document-subtype map this pass compiles against.
122
+ *
123
+ * Stated by the class rather than reached for through a module import, so
124
+ * every subtype decision the pass makes — the actor's own, and each
125
+ * embedded item reference's — reads one declaration that a subclass
126
+ * compiling for another system replaces.
127
+ *
128
+ * @type {import("./document-subtypes.mjs").DocumentSubtypeMap|undefined}
129
+ */
130
+ static documentSubtypes: import("./document-subtypes.mjs").DocumentSubtypeMap | undefined;
131
+ constructor({ itemsSourceDirs, foreignSourceDirs, ...options }: {
132
+ [x: string]: any;
133
+ itemsSourceDirs?: never[] | undefined;
134
+ foreignSourceDirs?: never[] | undefined;
135
+ });
136
+ /** @type {readonly string[]} */
137
+ itemsSourceDirs: readonly string[];
138
+ foreignSourceDirs: any;
139
+ /**
140
+ * This pass's system map, or a message naming the class that forgot it.
141
+ *
142
+ * @returns {import("./document-subtypes.mjs").DocumentSubtypeMap} The map.
143
+ */
144
+ get documentSubtypes(): import("./document-subtypes.mjs").DocumentSubtypeMap;
145
+ /**
146
+ * The frontmatter block this pass reads.
147
+ *
148
+ * @returns {string} The system's block name.
149
+ */
150
+ get system(): string;
151
+ itemsMap: Map<string, object> | undefined;
152
+ /** @inheritdoc */
153
+ reportDetail(stats: any): void;
154
+ /**
155
+ * The Foundry Item subtype an embedded reference's `type` addresses.
156
+ *
157
+ * **The reference is in the note vocabulary; the address is in the
158
+ * document's** (#140). An actor writes `(type, shortcode)` with the type an
159
+ * author authors, while {@link itemAddress} keys the predefined items by
160
+ * the subtype each compiled document carries — so exactly one of the two
161
+ * sides has to translate, and it is this one. The system's map is a
162
+ * function from note type to subtype by construction; the reverse is not,
163
+ * and a compiled document records nothing about the note that produced it,
164
+ * so there is no honest way to key the addresses the other way round.
165
+ *
166
+ * @param {string} type - The type the reference names.
167
+ * @returns {import("./document-subtypes.mjs").ReferencedSubtype} The
168
+ * subtype, or why the reference names none.
169
+ */
170
+ embeddedSubtype(type: string): import("./document-subtypes.mjs").ReferencedSubtype;
171
+ /**
172
+ * Resolve one embedded item from a `(type, shortcode?, overlay)`
173
+ * descriptor. If `shortcode` is given, the predefined item is fetched
174
+ * from `itemsMap` and the overlay deep-merged on top. If absent, the
175
+ * descriptor must carry enough fields to stand alone. The embedded
176
+ * item's `_id` is regenerated deterministically from
177
+ * `(actorId, subType, shortcode, indexKey)` so re-exports are stable —
178
+ * from the **document subtype**, so that renaming a note type (#78) leaves
179
+ * every embedded id exactly where it was.
180
+ * Returns null if the descriptor cannot be resolved.
181
+ *
182
+ * @param {Map<string, object>} itemsMap - The predefined items, by address.
183
+ * @param {string} actorId - The owning actor's id, seeding embedded ids.
184
+ * @param {string} type - The **note** type the reference names.
185
+ * @param {string|null} shortcode - The referenced item's shortcode, or
186
+ * `null` for a stand-alone entry.
187
+ * @param {object} [overlay] - The entry's remaining properties.
188
+ * @param {string} indexKey - Where the reference sits, for a diagnostic.
189
+ * It no longer reaches the id (#268) — it names the entry in a message.
190
+ * @param {string} ctx - Diagnostic context (the actor's label).
191
+ * @param {object} [at] - Where to locate a finding.
192
+ * @param {string} [at.fmKey] - The frontmatter key the reference sits
193
+ * under, so an unresolved one is reported at the reference rather than
194
+ * at the note.
195
+ * @returns {object|null} The embedded item, or null when it resolved to
196
+ * nothing — always with a finding emitted.
197
+ */
198
+ resolveEmbedded(itemsMap: Map<string, object>, actorId: string, type: string, shortcode: string | null, overlay?: object, indexKey: string, ctx: string, { fmKey }?: {
199
+ fmKey?: string | undefined;
200
+ }): object | null;
201
+ #private;
202
+ }
203
+ import { BasePackCompiler } from "./base-compiler.mjs";
204
+ export { extractAnchorSection, renderSection } from "./anchored-sections.mjs";
@@ -13,19 +13,22 @@ export function isAddressSegment(value: unknown): boolean;
13
13
  /**
14
14
  * The one charset every segment of a canonical address is held to (#59).
15
15
  *
16
- * An address is a hyphen-joined tuple — `sohl-skill-clmb` — and it is read back
17
- * by **counting segments**, with a fixed meaning per position. That is sound
18
- * for exactly one reason: the hyphen is *purely* a separator, because no
19
- * segment may contain one. Take that away and reading an address needs a
20
- * vocabulary to match against, a longest-match rule, and an answer for every
21
- * name that is a prefix of another — none of which exist.
16
+ * An address is a hyphen-joined tuple — `package-system-type-shortcode`, so
17
+ * `sohl-none-doc-gear` — and it is read back by **counting segments**, with a
18
+ * fixed meaning per position. That is sound for exactly one reason: the hyphen
19
+ * is *purely* a separator, because no segment may contain one. Take that away
20
+ * and reading an address needs a vocabulary to match against, a longest-match
21
+ * rule, and an answer for every name that is a prefix of another — none of
22
+ * which exist.
22
23
  *
23
24
  * So the charset is not a tidiness rule. It is the premise the address grammar
24
25
  * rests on, and the issue's word for how it should be held is **enforced rather
25
26
  * than assumed**: a value that breaks it is refused where it is written, not
26
27
  * discovered later as addresses that fail to parse and report nothing about
27
- * why. `harn-adventures` was that case — its keys read as four segments and
28
- * failed as a `null` return.
28
+ * why. `harn-adventures` was that case — a package whose own name carries the
29
+ * separator emits keys one segment too long, so `harn-adventures-sohl-skill-melee`
30
+ * counts five where the grammar requires four, and every one of them failed as
31
+ * a `null` return.
29
32
  *
30
33
  * This module is a **leaf with no local imports**, so the validator a
31
34
  * consumer's `package-build.config.mjs` reaches (`content-config.mjs`) can name
@@ -1,3 +1,18 @@
1
+ /**
2
+ * The one spelling of an address in this space: `type:shortcode`.
3
+ *
4
+ * Written by both readers — the compiled packs on each side of the diff, and
5
+ * the declarations read out of the tree — so a predecessor a note names and an
6
+ * address a pack publishes cannot come apart over punctuation or case. The
7
+ * shortcode is used **verbatim**, not lowercased: `readItemAddresses` reads it
8
+ * off the compiled document, where `Tabri` is stored as authored, and folding
9
+ * case here would join two addresses the packs keep apart.
10
+ *
11
+ * @param {string} type - The Foundry document subtype, not the note type.
12
+ * @param {string} shortcode - The address's `system.shortcode`.
13
+ * @returns {string} The address key.
14
+ */
15
+ export function itemAddressKey(type: string, shortcode: string): string;
1
16
  /**
2
17
  * The address space a set of compiled Item pack directories publishes.
3
18
  *
@@ -24,6 +39,9 @@ export function readItemAddresses(dirs: readonly string[]): Map<string, {
24
39
  shortcode: string;
25
40
  file: string;
26
41
  }>;
42
+ export function declaredPredecessors(contentBase: any, { skipDirectories, maps, config, records, problems }?: {
43
+ maps?: readonly import("./document-subtypes.mjs").DocumentSubtypeMap[] | undefined;
44
+ }): Map<any, any>;
27
45
  /**
28
46
  * Every address the baseline published that this build does not.
29
47
  *
@@ -36,12 +54,20 @@ export function readItemAddresses(dirs: readonly string[]): Map<string, {
36
54
  * @param {object} opts
37
55
  * @param {string} opts.baseline - What the baseline is, for the message —
38
56
  * conventionally `<package>@<version>`.
57
+ * @param {Map<string, {to: string, file: string}>} [opts.predecessors] - The
58
+ * declared renames, from {@link declaredPredecessors}. Omitted, the diff
59
+ * falls back to the id join alone and reports an unpinned rename as a
60
+ * withdrawal, which is what it did before #278.
39
61
  * @returns {Array<object>} One finding per departed address, in address order
40
- * so two runs read the same. `kind` is `"renamed"` (with `to`) or
41
- * `"withdrawn"`.
62
+ * so two runs read the same. `kind` is `"renamed"` (with `to`, and `declared`
63
+ * when it was the note's word rather than an id match) or `"withdrawn"`.
42
64
  */
43
- export function diffItemAddresses(baseline: Map<string, object>, current: Map<string, object>, { baseline: label }: {
65
+ export function diffItemAddresses(baseline: Map<string, object>, current: Map<string, object>, { baseline: label, predecessors }: {
44
66
  baseline: string;
67
+ predecessors?: Map<string, {
68
+ to: string;
69
+ file: string;
70
+ }> | undefined;
45
71
  }): Array<object>;
46
72
  /**
47
73
  * Every content note in a tree, indexed by the document id it authors.
@@ -53,11 +79,18 @@ export function diffItemAddresses(baseline: Map<string, object>, current: Map<st
53
79
  *
54
80
  * @param {string} contentBase - Root of the content tree.
55
81
  * @param {object} [opts]
56
- * @param {readonly string[]} [opts.skipDirectories] - Passed to the walk.
82
+ * @param {readonly string[]} [opts.skipDirectories] - The corpus scope, stated
83
+ * by the caller — see {@link addressCorpus}.
84
+ * @param {object} [opts.config] - The resolved build configuration, which the
85
+ * id is derived against. See {@link addressCorpus} for why that matters.
86
+ * @param {readonly object[]} [opts.records] - Index records the caller already
87
+ * derived, shared with {@link declaredPredecessors}.
57
88
  * @returns {Map<string, string>} Document id → the note's absolute path.
58
89
  */
59
- export function noteFilesById(contentBase: string, { skipDirectories }?: {
90
+ export function noteFilesById(contentBase: string, { skipDirectories, config, records, problems }?: {
60
91
  skipDirectories?: readonly string[] | undefined;
92
+ config?: object | undefined;
93
+ records?: readonly object[] | undefined;
61
94
  }): Map<string, string>;
62
95
  /**
63
96
  * Where to send the reader for one finding.
@@ -69,6 +102,11 @@ export function noteFilesById(contentBase: string, { skipDirectories }?: {
69
102
  * address existing. When neither is readable the position is **dropped**, never
70
103
  * defaulted to `1:1`.
71
104
  *
105
+ * A **declared** rename knows its note without any lookup — the declaration is
106
+ * how it was found — and is reported at the `renamedFrom:` line rather than the
107
+ * `shortcode:` line, because that is the line the finding is about and the one
108
+ * the author deletes once the declaration has done its work (#278).
109
+ *
72
110
  * @param {object} finding - One finding from {@link diffItemAddresses}.
73
111
  * @param {Map<string, string>} noteFiles - From {@link noteFilesById}.
74
112
  * @returns {{file?: string, line?: number, column?: number}} Spreadable
@@ -88,6 +126,16 @@ export function locateAddressFinding(finding: object, noteFiles: Map<string, str
88
126
  * known — and says so, rather than leaving the reader to wonder whether one was
89
127
  * looked for.
90
128
  *
129
+ * **A declared rename says it is declared** (#278). The two claims are not
130
+ * equally checkable: an id match is a fact in the artefacts, while a
131
+ * declaration is an author's word, and a reader deciding whether to trust the
132
+ * successor needs to know which one they have. Saying "the same document" of a
133
+ * declared rename would spend the id match's credibility on it.
134
+ *
135
+ * The consequence sentence is the same for both, because it is the same
136
+ * consequence: the old address stops resolving either way, and that is what
137
+ * the reader has to act on.
138
+ *
91
139
  * @param {object} finding - One finding from {@link diffItemAddresses}.
92
140
  * @returns {string} The message.
93
141
  */
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Extract the body of an H1 section whose heading carries the explicit
3
+ * anchor decorator `{#<anchorId>}`. Captures every line after the H1 up
4
+ * to (but not including) the next H1 — nested H2/H3 etc. and their bodies
5
+ * are included. The H1 line itself is discarded. Returns "" if no such
6
+ * heading exists. Fenced code blocks are respected so `# foo` inside
7
+ * ``` blocks does not trigger a match.
8
+ *
9
+ * @param {string} body - The note body.
10
+ * @param {string} anchorId - The anchor to find.
11
+ * @returns {string} The section's markdown, or "".
12
+ */
13
+ export function extractAnchorSection(body: string, anchorId: string): string;
14
+ /**
15
+ * Render an extracted markdown section to HTML, or "" if empty.
16
+ *
17
+ * @param {string} body - The note body.
18
+ * @param {string} anchorId - The anchor to find.
19
+ * @returns {string} The rendered HTML, or "".
20
+ */
21
+ export function renderSection(body: string, anchorId: string): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The `{#slug}` anchors a note's body declares, with where each one sits.
3
+ *
4
+ * Only headings carrying an explicit anchor are collected. A bare `#` heading
5
+ * also starts a journal page, but it declares no slug, so nothing can address
6
+ * it with `#…` — listing it would offer a link that cannot be written.
7
+ *
8
+ * @param {string} body - The note's markdown body, frontmatter already removed.
9
+ * @param {number} [bodyLine] - The 1-based file line the body starts on, from
10
+ * `parseMarkdownFile`. Anchors are reported at their position in the **file**,
11
+ * so an editor can jump straight to one; passing nothing numbers from the body.
12
+ * @returns {Array<{slug: string, name: string, level: number, line: number}>}
13
+ * In document order.
14
+ */
15
+ export function collectAnchors(body: string, bodyLine?: number): Array<{
16
+ slug: string;
17
+ name: string;
18
+ level: number;
19
+ line: number;
20
+ }>;
@@ -103,6 +103,8 @@ export class BasePackCompiler {
103
103
  * @param {object} options
104
104
  * @param {string} options.contentBase - Root of the content tree.
105
105
  * @param {string} options.dest - Where this pass writes its JSON.
106
+ * @param {readonly string[]} options.skipDirectories - Directories the walk
107
+ * never descends into. Required: see {@link assertStatedScope}.
106
108
  * @param {(path: string|null) => string|null} [options.folderResolver] -
107
109
  * Resolves a `sohl.folder` id against this pack's folder hierarchy.
108
110
  * @param {string} [options.packName] - The pack this pass writes.
@@ -112,9 +114,10 @@ export class BasePackCompiler {
112
114
  * @param {boolean} [options.routingReporter] - Whether this pass reports a
113
115
  * note of its type that routes nowhere.
114
116
  */
115
- constructor({ contentBase, dest, folderResolver, packName, packSystem, docType, router, routingReporter, }?: {
117
+ constructor({ contentBase, dest, skipDirectories, folderResolver, packName, packSystem, docType, router, routingReporter, corpus, }?: {
116
118
  contentBase: string;
117
119
  dest: string;
120
+ skipDirectories: readonly string[];
118
121
  folderResolver?: ((path: string | null) => string | null) | undefined;
119
122
  packName?: string | undefined;
120
123
  docType?: string | undefined;
@@ -189,6 +192,7 @@ export class BasePackCompiler {
189
192
  */
190
193
  unresolvedLinks: number;
191
194
  packSystem: any;
195
+ corpus: any;
192
196
  /**
193
197
  * The `_stats` block every entry this pass emits is stamped with (#48).
194
198
  *
@@ -224,9 +228,12 @@ export class BasePackCompiler {
224
228
  * instances of.
225
229
  *
226
230
  * @param {object} fm - The note's frontmatter.
227
- * @returns {boolean} True when the note may be compiled here.
228
- * @throws {Error} When this pack's system is absent from the note. The
229
- * error carries a `position` where the note's own file can be read.
231
+ * @returns {boolean} True when the note may be compiled here; `false` when
232
+ * it belongs to another system's pass of the same document type, which is
233
+ * skipped as quietly as any other note this pack does not own.
234
+ * @throws {Error} When this pack's system is absent from the note and no
235
+ * other configured system claims it. The error carries a `position` where
236
+ * the note's own file can be read.
230
237
  */
231
238
  eligibleFor(fm: object): boolean;
232
239
  /**
@@ -267,17 +274,10 @@ export class BasePackCompiler {
267
274
  * @returns {Promise<void>}
268
275
  */
269
276
  prepare(): Promise<void>;
270
- linkIndex: {
271
- byShortcode: Map<any, any>;
272
- types: Set<any>;
273
- } | undefined;
274
- contentDocs: {
275
- fm: object;
276
- path: string;
277
- tld: string;
278
- folder: string;
279
- absPath: string;
280
- }[] | undefined;
277
+ reportsCorpusProblems: boolean | undefined;
278
+ linkIndex: any;
279
+ contentDocs: any;
280
+ sqlTables: any;
281
281
  /**
282
282
  * The body {@link BasePackCompiler#buildEntry} receives.
283
283
  *
@@ -344,7 +344,7 @@ export class BasePackCompiler {
344
344
  * The sibling of {@link BasePackCompiler#reportUndeclaredSystemData}, and
345
345
  * the half that sees what no declaration states. A compiler writes keys of
346
346
  * its own alongside the declared fields — `shortcode`, `actionDefs`,
347
- * `notes`, `docHtml`, `archetype` — and neither the field-declaration check
347
+ * `notes`, `docHtml`, `templatePriority` — and neither the field-declaration check
348
348
  * nor the authored-`system` check can see them, so until this nothing
349
349
  * compared them at all. Foundry's discard is the same silent one either
350
350
  * way.
@@ -369,7 +369,7 @@ export class BasePackCompiler {
369
369
  * @param {object} [opts.config] - The resolved build configuration.
370
370
  * @returns {number} How many findings were new to this pass.
371
371
  */
372
- reportEmittedSystemData(system: object, { fm, block, documentType, subType, type, fields, config }: {
372
+ reportEmittedSystemData(emitted: any, { fm, block, documentType, subType, type, fields, config }: {
373
373
  fm: object;
374
374
  block: string;
375
375
  documentType: string;