@heroiclands/package-build 0.6.1 → 3.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 (127) hide show
  1. package/CHANGELOG-content-build.md +965 -0
  2. package/CHANGELOG.md +58 -0
  3. package/CONTENT.md +824 -0
  4. package/MIGRATING.md +127 -0
  5. package/README.md +29 -17
  6. package/bin/content-build.mjs +1082 -0
  7. package/bin/package-build.mjs +2 -2
  8. package/bin/report.mjs +2 -2
  9. package/config.mjs +21 -18
  10. package/content-config.mjs +1424 -0
  11. package/coverage.mjs +1 -1
  12. package/e2e.mjs +1 -1
  13. package/engine/abbreviations.mjs +0 -0
  14. package/engine/base-compiler.mjs +608 -0
  15. package/engine/code-fences.mjs +311 -0
  16. package/engine/compendiums.mjs +353 -0
  17. package/engine/content-address.mjs +141 -0
  18. package/engine/content-links.mjs +438 -0
  19. package/engine/content-lint.mjs +204 -0
  20. package/engine/content-package.mjs +67 -0
  21. package/engine/content-slug.mjs +169 -0
  22. package/engine/content-tables.mjs +1216 -0
  23. package/engine/content-tree.mjs +56 -0
  24. package/engine/diagnostics.mjs +270 -0
  25. package/engine/field-reference.mjs +242 -0
  26. package/engine/field-spec.mjs +218 -0
  27. package/engine/foreign-catalog.mjs +450 -0
  28. package/engine/foreign-manifests.mjs +129 -0
  29. package/engine/frontmatter-lint.mjs +343 -0
  30. package/engine/frontmatter.mjs +258 -0
  31. package/engine/generate.mjs +305 -0
  32. package/engine/helpers.mjs +809 -0
  33. package/engine/ids.mjs +192 -0
  34. package/engine/index.mjs +142 -0
  35. package/engine/item-docs.mjs +153 -0
  36. package/engine/item-registry.mjs +129 -0
  37. package/engine/journals.mjs +374 -0
  38. package/engine/kb-manifest.mjs +473 -0
  39. package/engine/macros.mjs +333 -0
  40. package/engine/manifest-emit.mjs +339 -0
  41. package/engine/map-notes.mjs +1302 -0
  42. package/engine/pack-config.mjs +479 -0
  43. package/engine/pack-router.mjs +254 -0
  44. package/engine/prose-config.mjs +238 -0
  45. package/engine/prose-lint.mjs +294 -0
  46. package/engine/region-events.mjs +79 -0
  47. package/engine/scene-levels.mjs +420 -0
  48. package/engine/scenes.mjs +528 -0
  49. package/engine/site-build.mjs +778 -0
  50. package/engine/site-index.mjs +332 -0
  51. package/engine/web-wikilinks.mjs +330 -0
  52. package/engine/wikilink-syntax.mjs +105 -0
  53. package/engine/wikilinks.mjs +645 -0
  54. package/index.mjs +42 -25
  55. package/lang.mjs +1 -1
  56. package/manifest.mjs +79 -5
  57. package/markdownlint-config.mjs +45 -0
  58. package/package.json +78 -35
  59. package/prettier-config.mjs +34 -0
  60. package/sohl/actors.mjs +581 -0
  61. package/sohl/affiliation-standings.mjs +44 -0
  62. package/sohl/being-info.mjs +173 -0
  63. package/sohl/default-item-art.mjs +82 -0
  64. package/sohl/index.mjs +55 -0
  65. package/sohl/item-builders.mjs +114 -0
  66. package/sohl/item-fields.mjs +867 -0
  67. package/sohl/items.mjs +197 -0
  68. package/sohl/kb-passes.mjs +207 -0
  69. package/sohl/note-schemas.mjs +322 -0
  70. package/templates.mjs +1 -1
  71. package/types/config.d.mts +1 -1
  72. package/types/content-config.d.mts +571 -0
  73. package/types/engine/abbreviations.d.mts +44 -0
  74. package/types/engine/base-compiler.d.mts +320 -0
  75. package/types/engine/code-fences.d.mts +102 -0
  76. package/types/engine/compendiums.d.mts +77 -0
  77. package/types/engine/content-address.d.mts +61 -0
  78. package/types/engine/content-links.d.mts +87 -0
  79. package/types/engine/content-lint.d.mts +47 -0
  80. package/types/engine/content-package.d.mts +36 -0
  81. package/types/engine/content-slug.d.mts +59 -0
  82. package/types/engine/content-tables.d.mts +131 -0
  83. package/types/engine/content-tree.d.mts +12 -0
  84. package/types/engine/diagnostics.d.mts +152 -0
  85. package/types/engine/field-reference.d.mts +20 -0
  86. package/types/engine/field-spec.d.mts +181 -0
  87. package/types/engine/foreign-catalog.d.mts +103 -0
  88. package/types/engine/foreign-manifests.d.mts +43 -0
  89. package/types/engine/frontmatter-lint.d.mts +74 -0
  90. package/types/engine/frontmatter.d.mts +99 -0
  91. package/types/engine/generate.d.mts +56 -0
  92. package/types/engine/helpers.d.mts +348 -0
  93. package/types/engine/ids.d.mts +126 -0
  94. package/types/engine/index.d.mts +33 -0
  95. package/types/engine/item-docs.d.mts +73 -0
  96. package/types/engine/item-registry.d.mts +57 -0
  97. package/types/engine/journals.d.mts +121 -0
  98. package/types/engine/kb-manifest.d.mts +214 -0
  99. package/types/engine/macros.d.mts +201 -0
  100. package/types/engine/manifest-emit.d.mts +137 -0
  101. package/types/engine/map-notes.d.mts +283 -0
  102. package/types/engine/pack-config.d.mts +77 -0
  103. package/types/engine/pack-router.d.mts +53 -0
  104. package/types/engine/prose-config.d.mts +121 -0
  105. package/types/engine/prose-lint.d.mts +59 -0
  106. package/types/engine/region-events.d.mts +43 -0
  107. package/types/engine/scene-levels.d.mts +36 -0
  108. package/types/engine/scenes.d.mts +48 -0
  109. package/types/engine/site-build.d.mts +214 -0
  110. package/types/engine/site-index.d.mts +141 -0
  111. package/types/engine/web-wikilinks.d.mts +67 -0
  112. package/types/engine/wikilink-syntax.d.mts +101 -0
  113. package/types/engine/wikilinks.d.mts +172 -0
  114. package/types/index.d.mts +3 -0
  115. package/types/manifest.d.mts +52 -4
  116. package/types/markdownlint-config.d.mts +8 -0
  117. package/types/prettier-config.d.mts +1 -0
  118. package/types/sohl/actors.d.mts +65 -0
  119. package/types/sohl/affiliation-standings.d.mts +22 -0
  120. package/types/sohl/being-info.d.mts +78 -0
  121. package/types/sohl/default-item-art.d.mts +28 -0
  122. package/types/sohl/index.d.mts +7 -0
  123. package/types/sohl/item-builders.d.mts +11 -0
  124. package/types/sohl/item-fields.d.mts +20 -0
  125. package/types/sohl/items.d.mts +19 -0
  126. package/types/sohl/kb-passes.d.mts +71 -0
  127. package/types/sohl/note-schemas.d.mts +7 -0
@@ -0,0 +1,172 @@
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 (#1362). `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 (#1514).
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 (#1276), 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 is a bare alias instead.
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`** — the canonical form (#1398). Obsidian reads `/` inside
35
+ * a wikilink as a *path* and resolves it against the vault's folders, so a
36
+ * slash-qualified link is a broken link in the editor where the content is now
37
+ * authored. A hyphen qualifies **only when what precedes it is a known type**:
38
+ * note names contain hyphens too (`Grukar-ahk`), and those must keep resolving
39
+ * as aliases. The split is at the **first** hyphen, so a shortcode may itself
40
+ * contain one (`trauma-self-pro` → `trauma` + `self-pro`).
41
+ * - **`type/shortcode`** — the legacy form, still resolved so that a link
42
+ * written before the vault migrated does not silently die. A slash is
43
+ * *unconditionally* a qualifier: nothing else uses one, so an unknown type
44
+ * before it is an error rather than an invitation to try the alias index. The
45
+ * split is at the **last** slash, as it always was.
46
+ *
47
+ * A leading **package** segment is optional and outermost: `sohl-skill-lang` is
48
+ * `skill-lang` in the `sohl` package. It is read only when `packages` is given
49
+ * and names the segment, and only when the remainder is itself a valid address,
50
+ * so a note called "Grukar-ahk" stays an alias (#1499).
51
+ *
52
+ * @param {string} target - The link target, anchor already removed.
53
+ * @param {Set<string>} types - Every type the content tree contains.
54
+ * @param {Set<string>} [packages] - Every package an address may name. Omitted
55
+ * by callers that resolve within one package, where the form cannot occur.
56
+ * @returns {{type: string, shortcode: string, itemDoc: boolean,
57
+ * package?: string, reason?: undefined} | {reason: "unknown-type"} | null}
58
+ * The resolved qualifier; a `reason` when the target is definitely qualified
59
+ * but names no known type; or `null` when it is a bare alias.
60
+ */
61
+ export function readQualifier(target: string, types: Set<string>, packages?: Set<string>): {
62
+ type: string;
63
+ shortcode: string;
64
+ itemDoc: boolean;
65
+ package?: string;
66
+ reason?: undefined;
67
+ } | {
68
+ reason: "unknown-type";
69
+ } | null;
70
+ /**
71
+ * The deterministic JournalEntryPage id for one anchor: SHA-256 of
72
+ * `"<noteId>-<anchorSlug>"`, base64-encoded, reduced to the 16 alphanumeric
73
+ * characters a Foundry id allows.
74
+ *
75
+ * Base64's `+`, `/`, and `=` are **not** legal in a Foundry document id
76
+ * (`/^[A-Za-z0-9]{16}$/`), so they are dropped before the first 16 characters
77
+ * are taken — the value stays a pure function of its two inputs, which is what
78
+ * lets the link and the page be computed independently.
79
+ *
80
+ * @param {string} noteId - The owning JournalEntry's `_id`.
81
+ * @param {string} anchorSlug - The slug declared by `{#slug}` on the heading.
82
+ * @returns {string} A 16-character alphanumeric id.
83
+ */
84
+ export function anchorPageId(noteId: string, anchorSlug: string): string;
85
+ /**
86
+ * Builds the link-resolution tables for a content tree.
87
+ *
88
+ * @param {Array<{type: string, id: string, shortcode?: string|null,
89
+ * aliases?: string[], name?: string, pack?: string, docPack?: string}>} docs -
90
+ * One entry per content note. `pack` / `docPack` name the packs the note's
91
+ * document and its documentation entry landed in; omitted, the conventional
92
+ * one-pack-per-type names stand in.
93
+ * @param {string} packageId - The Foundry package shipping the packs; the first
94
+ * segment of every emitted UUID.
95
+ * @param {Map<string, object>} [foreign] - Canonically keyed entries from
96
+ * vendored manifests of packages this build links into but does not publish.
97
+ * @param {string} [contentPackage] - This build's *content* package, which an
98
+ * authored address may name explicitly. Defaults to `packageId`.
99
+ * @returns {{byShortcode: Map<string, object>, byAlias: Map<string, object|null>,
100
+ * types: Set<string>}} `byAlias` holds `null` where a type-scoped alias is
101
+ * claimed by more than one document, which makes the bare `[[Text]]` form
102
+ * unusable for it. `types` is every type the tree actually contains, so a
103
+ * qualifier naming no real type can be told apart from a missing target.
104
+ */
105
+ export function buildWikilinkIndex(docs: Array<{
106
+ type: string;
107
+ id: string;
108
+ shortcode?: string | null;
109
+ aliases?: string[];
110
+ name?: string;
111
+ pack?: string;
112
+ docPack?: string;
113
+ }>, packageId: string, foreign?: Map<string, object>, contentPackage?: string): {
114
+ byShortcode: Map<string, object>;
115
+ byAlias: Map<string, object | null>;
116
+ types: Set<string>;
117
+ };
118
+ /** Matches a whole wikilink, capturing its inner text. */
119
+ /**
120
+ * Rewrites every wikilink in a markdown body as a Foundry UUID enricher.
121
+ *
122
+ * A link that cannot be resolved is left exactly as it was and reported in
123
+ * `unresolved`, so a content gap degrades to visible literal text rather than
124
+ * a broken link or a failed build.
125
+ *
126
+ * **Code is verbatim.** A `[[…]]` inside a fenced or indented code block, or
127
+ * inside an inline code span, is source text an author wrote to be read as
128
+ * written, so it is left alone and not reported (#1505). Without that, a
129
+ * script sample containing `grid[[0]]` became a link — and only for some
130
+ * array shapes, `[[1,2],[3,4]]` having an inner `]` the pattern cannot cross,
131
+ * so the corruption looked arbitrary. It reaches the reader through the
132
+ * *documented* copy of a macro while the executable copy stays correct.
133
+ *
134
+ * @param {string} markdown - The note body (frontmatter already stripped).
135
+ * @param {object} ctx
136
+ * @param {string} ctx.type - The source note's `type`, which scopes a bare `[[Text]]`.
137
+ * @param {string} ctx.id - The source note's document id.
138
+ * @param {string} [ctx.pack] - The pack the source note's own document landed
139
+ * in, which addresses a `[[#slug]]` self-link — the one target with no index
140
+ * entry.
141
+ * @param {string} [ctx.docPack] - The pack the source note's documentation
142
+ * entry landed in.
143
+ * @param {{byShortcode: Map, byAlias: Map, types: Set}} ctx.index - From
144
+ * {@link buildWikilinkIndex}.
145
+ * @returns {{markdown: string, unresolved: Array<{link: string, target: string,
146
+ * offset: number, reason: "unknown"|"ambiguous"|"unknown-type"}>}} `offset`
147
+ * is the link's 0-based position in `markdown`, which is what lets a caller
148
+ * report the line and column it sits on (#17).
149
+ */
150
+ export function convertWikilinks(markdown: string, { type, id, pack, docPack, index }: {
151
+ type: string;
152
+ id: string;
153
+ pack?: string | undefined;
154
+ docPack?: string | undefined;
155
+ index: {
156
+ byShortcode: Map<any, any>;
157
+ byAlias: Map<any, any>;
158
+ types: Set<any>;
159
+ };
160
+ }): {
161
+ markdown: string;
162
+ unresolved: Array<{
163
+ link: string;
164
+ target: string;
165
+ offset: number;
166
+ reason: "unknown" | "ambiguous" | "unknown-type";
167
+ }>;
168
+ };
169
+ import { ITEM_PACK } from "./ids.mjs";
170
+ import { PACK_BY_TYPE } from "./ids.mjs";
171
+ import { packForType } from "./ids.mjs";
172
+ export { ITEM_PACK, PACK_BY_TYPE, packForType };
package/types/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ export * as engine from "./engine/index.mjs";
2
+ export * as sohl from "./sohl/index.mjs";
1
3
  export * as manifest from "./manifest.mjs";
2
4
  export * as bundle from "./bundle.mjs";
3
5
  export * as stage from "./stage.mjs";
@@ -8,3 +10,4 @@ export * as e2e from "./e2e.mjs";
8
10
  export * as lang from "./lang.mjs";
9
11
  export * as coverage from "./coverage.mjs";
10
12
  export * as templates from "./templates.mjs";
13
+ export { defineConfig, PACKAGE_KINDS, PACK_DOCUMENT_TYPES } from "./content-config.mjs";
@@ -45,7 +45,7 @@ export function releaseUrls({ repoUrl, version, artifact }: {
45
45
  /**
46
46
  * The manifest's `packs`, derived from the one pack list the build already has.
47
47
  *
48
- * The two used to be written separately — `content-build.config.yaml` declared
48
+ * The two used to be written separately — `package-build.config.yaml` declared
49
49
  * a pack's name and type, and the manifest template declared them again beside
50
50
  * a label, a path and a system id, with nothing checking that the pairs agreed.
51
51
  * They are one list now.
@@ -55,10 +55,26 @@ export function releaseUrls({ repoUrl, version, artifact }: {
55
55
  * ships as an ordinary compendium. The order matches `packDirectories`, so the
56
56
  * manifest lists packs in the order the build compiles them.
57
57
  *
58
- * @param {object} config - The resolved content-build configuration.
58
+ * @param {object} config - The resolved content configuration.
59
59
  * @returns {object[]} The manifest's `packs` array.
60
60
  */
61
61
  export function manifestPacks(config: object): object[];
62
+ /**
63
+ * The `relationships` block as published — every declared dependency, with the
64
+ * build's own keys dropped.
65
+ *
66
+ * Shape is otherwise preserved: kinds keep their order and their entries, an
67
+ * entry keeps its remaining keys in the order it declared them, and a block
68
+ * carrying no build-only key comes back equal to what went in. Only
69
+ * {@link BUILD_ONLY_RELATIONSHIP_KEYS} are removed — an unrecognised key is
70
+ * left alone, on the same reasoning that lets a declared manifest key through
71
+ * unread: a key Foundry adds later should not need a release of this package.
72
+ *
73
+ * @param {Record<string, unknown>} relationships - The declared block, as
74
+ * content-build resolved it.
75
+ * @returns {Record<string, unknown>} It, without the build-only keys.
76
+ */
77
+ export function publishedRelationships(relationships: Record<string, unknown>): Record<string, unknown>;
62
78
  /**
63
79
  * Build a Foundry package manifest from the resolved configuration.
64
80
  *
@@ -73,8 +89,12 @@ export function manifestPacks(config: object): object[];
73
89
  * - **Computed** — namespaced `flags` a repository works out for itself, merged
74
90
  * over any it declared.
75
91
  *
92
+ * `relationships` is derived but not copied whole: the keys that direct the
93
+ * build rather than describe the package are dropped first — see
94
+ * {@link publishedRelationships}.
95
+ *
76
96
  * @param {object} options - Inputs.
77
- * @param {object} options.config - The resolved content-build configuration.
97
+ * @param {object} options.config - The resolved content configuration.
78
98
  * @param {object} options.packageJson - The repository's `package.json`.
79
99
  * @param {string} options.artifact - `system` or `module`.
80
100
  * @param {Record<string, object>} [options.flags] - Namespaced flags to merge.
@@ -90,7 +110,7 @@ export function buildManifest({ config, packageJson, artifact, flags }: {
90
110
  * Write the generated manifest into the staged package.
91
111
  *
92
112
  * @param {object} options - As {@link buildManifest}, plus where to write.
93
- * @param {object} options.config - The resolved content-build configuration.
113
+ * @param {object} options.config - The resolved content configuration.
94
114
  * @param {object} options.packageJson - The repository's `package.json`.
95
115
  * @param {string} options.artifact - `system` or `module`.
96
116
  * @param {string} options.outDir - Directory to write into.
@@ -116,3 +136,31 @@ export function writeManifest({ config, packageJson, artifact, outDir, flags, }:
116
136
  * convention this project is free to choose.
117
137
  */
118
138
  export const ARTIFACTS: readonly string[];
139
+ /**
140
+ * Relationship keys that direct the **build**, rather than describe the
141
+ * package.
142
+ *
143
+ * `relationships` is the one manifest block with a second reader.
144
+ * `@heroiclands/content-build` consumes it too, and v1.8.0 added
145
+ * `itemCatalog: true` as an opt-in on a declared dependency
146
+ * (content-build#82): it selects that package's Item packs as a resolution
147
+ * source for the actors pass. That is an instruction to the build, not a fact
148
+ * about the shipped package — Foundry's relationship schema does not define
149
+ * it, and someone reading a published manifest cannot tell a build directive
150
+ * from a declaration about what the package needs.
151
+ *
152
+ * So the block is filtered rather than copied whole (#29). The rule is the
153
+ * distinction, not the name: a key listed here answers *how is this built?*,
154
+ * and every key that survives answers *what does this package depend on?*.
155
+ * `itemCatalog` is the first build-time key to land on a relationship and is
156
+ * unlikely to be the last.
157
+ *
158
+ * A list is enough, and needs no prefix agreed between the two packages,
159
+ * because the input is already closed: content-build normalises a relationship
160
+ * to `id`, `type`, `manifest`, `compatibility` and its own build keys, and
161
+ * rejects anything else at configuration time. A key that reaches here is one
162
+ * the toolchain itself put there.
163
+ *
164
+ * @type {readonly string[]}
165
+ */
166
+ export const BUILD_ONLY_RELATIONSHIP_KEYS: readonly string[];
@@ -0,0 +1,8 @@
1
+ declare namespace _default {
2
+ export { MARKDOWNLINT_CONFIG as config };
3
+ export let globs: string[];
4
+ export let ignores: string[];
5
+ export let gitignore: boolean;
6
+ }
7
+ export default _default;
8
+ import { MARKDOWNLINT_CONFIG } from "./engine/prose-config.mjs";
@@ -0,0 +1 @@
1
+ export { PRETTIER_CONFIG as default } from "./engine/prose-config.mjs";
@@ -0,0 +1,65 @@
1
+ export class Actors extends BasePackCompiler {
2
+ constructor({ itemsSourceDirs, foreignSourceDirs, ...options }: {
3
+ [x: string]: any;
4
+ itemsSourceDirs: any;
5
+ foreignSourceDirs?: never[] | undefined;
6
+ });
7
+ /** @type {readonly string[]} */
8
+ itemsSourceDirs: readonly string[];
9
+ foreignSourceDirs: any;
10
+ itemsMap: Map<string, object> | undefined;
11
+ /** @inheritdoc */
12
+ reportDetail(stats: any): void;
13
+ /**
14
+ * Resolve one embedded item from a `(type, shortcode?, overlay)`
15
+ * descriptor. If `shortcode` is given, the predefined item is fetched
16
+ * from `itemsMap` and the overlay deep-merged on top. If absent, the
17
+ * descriptor must carry enough fields to stand alone. The embedded
18
+ * item's `_id` is regenerated deterministically from
19
+ * `(actorId, type, shortcode, indexKey)` so re-exports are stable.
20
+ * Returns null if the descriptor cannot be resolved.
21
+ */
22
+ resolveEmbedded(itemsMap: any, actorId: any, type: any, shortcode: any, overlay: any, indexKey: any, ctx: any): any;
23
+ /**
24
+ * Build all embedded items for an actor: one per `sohl.attributes`
25
+ * entry plus one per `sohl.items` entry. `sohl.skills` is ignored.
26
+ */
27
+ buildEmbeddedItems(itemsMap: any, actorId: any, fm: any, ctx: any): any[];
28
+ buildBeing(itemsMap: any, fm: any, body: any): {
29
+ name: any;
30
+ type: string;
31
+ img: string;
32
+ _id: any;
33
+ system: {
34
+ shortcode: any;
35
+ portrait: string;
36
+ appearance: any;
37
+ dossier: any;
38
+ };
39
+ items: any[];
40
+ prototypeToken: {
41
+ name: any;
42
+ displayName: number;
43
+ actorLink: boolean;
44
+ texture: {
45
+ src: string;
46
+ };
47
+ width: number;
48
+ height: number;
49
+ sight: {
50
+ enabled: boolean;
51
+ };
52
+ detectionModes: never[];
53
+ };
54
+ effects: never[];
55
+ folder: string | null;
56
+ sort: number;
57
+ ownership: {
58
+ default: number;
59
+ };
60
+ flags: object;
61
+ _stats: object;
62
+ _key: string;
63
+ };
64
+ }
65
+ import { BasePackCompiler } from "../engine/base-compiler.mjs";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The affiliation standings an authored `relation` map may use — one
3
+ * affiliation's stance toward another (#1404).
4
+ *
5
+ * Plain ESM for the same reason `./default-item-art.mjs` is: the pack scripts
6
+ * run under bare `node`, so they cannot read the runtime's TypeScript
7
+ * `AFFILIATION_STANDING`. This module is where the pipeline reads the list
8
+ * from, rather than restating it inside a compiler (#1510).
9
+ *
10
+ * The runtime keeps its own `defineType("SOHL.Affiliation.Standing", { … })`
11
+ * declaration in `src/utils/constants.ts`, because that literal is *parsed out
12
+ * of the source* by two build guards — the generated type catalog
13
+ * (`utils/build-type-catalog.mjs`) and the localization-coverage check
14
+ * (`utils/check-lang-coverage.mjs`) — and replacing it with an imported
15
+ * reference would blind both. The two lists are held identical by a test
16
+ * (`tests/build/src-import-severance.test.ts`), so the drift the
17
+ * duplication would otherwise allow is a failing build rather than an
18
+ * affiliation whose authored hostility quietly became neutrality.
19
+ *
20
+ * @type {readonly string[]}
21
+ */
22
+ export const AFFILIATION_STANDINGS: readonly string[];
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Whether a note's frontmatter describes a being.
3
+ *
4
+ * @param {{type?: unknown}|null|undefined} fm - A note's frontmatter.
5
+ * @returns {boolean} `true` when the note is a being.
6
+ */
7
+ export function isBeing(fm: {
8
+ type?: unknown;
9
+ } | null | undefined): boolean;
10
+ /**
11
+ * Derive a being's info-block fields from its raw `sohl.items[]`.
12
+ *
13
+ * Each item's `shortcode` is resolved against `index` — keyed
14
+ * `"<type>:<shortcode>"` — for a display name and a link to the item's own
15
+ * page. `attributes` already match the sidebar shape and pass through
16
+ * untouched.
17
+ *
18
+ * **Authored values win.** Only fields the author did not supply are derived,
19
+ * so a note that hand-writes `sohl.skills` keeps exactly what it wrote. An
20
+ * item's inline `name` beats the index, and an unresolved shortcode falls back
21
+ * to *itself* rather than being dropped — a page that names an item the index
22
+ * has not heard of is better than a page silently missing a row.
23
+ *
24
+ * Returns a new object; the input is not mutated.
25
+ *
26
+ * @param {object|null|undefined} sohl - The note's `sohl` frontmatter block.
27
+ * @param {Map<string, {name?: string, url?: string}>} index - Content index,
28
+ * `"<type>:<shortcode>"` → the item's page.
29
+ * @returns {object|null|undefined} The block with its info-block fields filled
30
+ * in, or the input unchanged when there is nothing to derive from.
31
+ */
32
+ export function deriveBeingInfo(sohl: object | null | undefined, index: Map<string, {
33
+ name?: string;
34
+ url?: string;
35
+ }>): object | null | undefined;
36
+ /**
37
+ * **A being's info-block fields**, derived from the items it embeds.
38
+ *
39
+ * A `being` note carries its embedded documents as `sohl.items` — a flat list
40
+ * of `{ shortcode, type, system? }` — but the shared theme's sidebar reads
41
+ * *resolved* shapes: a `skills` map, `gear` grouped by kind, and `spells` /
42
+ * `talents` split out of the mystical abilities. This is the translation
43
+ * between the two, and it is SoHL data-model knowledge: which item type is a
44
+ * skill, where a mastery level lives, what distinguishes a spell from a talent.
45
+ *
46
+ * **It lives here because it was living in two places.** Both
47
+ * `Song-of-Heroic-Lands-FoundryVTT` and `sohl-thalorna` carried a copy, and the
48
+ * copies drifted: SoHL's caller still gated the derivation on `character` and
49
+ * `creature`, the two types #1580 merged into `being`, so it had matched
50
+ * nothing since the merge and all 95 of its being pages published with empty
51
+ * sidebar sections (SoHL#1696). thalorna's copy checked `being` and was right.
52
+ * Nothing failed in either repository; the pages built and shipped.
53
+ *
54
+ * {@link isBeing} exists for that reason. The bug was not in the derivation —
55
+ * it was in each caller's idea of what a being *is*, written out per repository
56
+ * where it could rot independently. One definition, imported.
57
+ *
58
+ * @module
59
+ */
60
+ /**
61
+ * The note `type` whose pages carry a being info block.
62
+ *
63
+ * One name, since #1580 merged `character` and `creature` into the `being` they
64
+ * had always compiled into. The retired names are deliberately **not** accepted
65
+ * as aliases: they throw elsewhere in the system, and tolerating them here
66
+ * would hide the next drift of this kind rather than surface it.
67
+ */
68
+ export const BEING_TYPE: "being";
69
+ /**
70
+ * The sidebar group each gear item type is displayed under.
71
+ *
72
+ * Presentation naming, not data-model naming: the model says `weapongear`, the
73
+ * sidebar heading says "weapons". Kept as one table so a new gear type is added
74
+ * in a single place rather than in each consumer's site build.
75
+ *
76
+ * @type {Readonly<Record<string, string>>}
77
+ */
78
+ export const GEAR_TYPE_TO_KEY: Readonly<Record<string, string>>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The default art path for an item `type`, or throw when the type is unknown —
3
+ * the build's fail-fast contract, so an unrecognized type is never silently
4
+ * defaulted (aborting the pack build rather than shipping a mismatched icon).
5
+ *
6
+ * Runtime callers that must tolerate unknown or `base`-typed items should read
7
+ * {@link DEFAULT_ITEM_ART} directly and fall back to Foundry's default instead
8
+ * of calling this — see `SohlItem.getDefaultArtwork`.
9
+ *
10
+ * @param {string} type - the item type.
11
+ * @returns {string} the default image path for that type.
12
+ */
13
+ export function defaultItemArt(type: string): string;
14
+ export namespace DEFAULT_ITEM_ART {
15
+ let affiliation: string;
16
+ let affliction: string;
17
+ let armorgear: string;
18
+ let attribute: string;
19
+ let concoctiongear: string;
20
+ let containergear: string;
21
+ let miscgear: string;
22
+ let mystery: string;
23
+ let mysticalability: string;
24
+ let projectilegear: string;
25
+ let skill: string;
26
+ let trauma: string;
27
+ let weapongear: string;
28
+ }
@@ -0,0 +1,7 @@
1
+ export * as itemBuilders from "./item-builders.mjs";
2
+ export * as items from "./items.mjs";
3
+ export * as actors from "./actors.mjs";
4
+ export * as kbPasses from "./kb-passes.mjs";
5
+ export { AFFILIATION_STANDINGS } from "./affiliation-standings.mjs";
6
+ export { DEFAULT_ITEM_ART, defaultItemArt } from "./default-item-art.mjs";
7
+ export { BEING_TYPE, GEAR_TYPE_TO_KEY, deriveBeingInfo, isBeing } from "./being-info.mjs";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Every item type, paired with the builder for its `system` block, the default
3
+ * art for the type, and the frontmatter fields it declares.
4
+ *
5
+ * @type {Readonly<Record<string, Readonly<{system: (fm: object) => object, img: string, fields: readonly object[]}>>>}
6
+ */
7
+ export const ITEM_BUILDERS: Readonly<Record<string, Readonly<{
8
+ system: (fm: object) => object;
9
+ img: string;
10
+ fields: readonly object[];
11
+ }>>>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Every item type's frontmatter vocabulary, in the order the `system` block
3
+ * emits it.
4
+ *
5
+ * @type {Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>}
6
+ */
7
+ export const ITEM_FIELDS: Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>;
8
+ /**
9
+ * The one conditional field in the vocabulary: a combat technique's strike
10
+ * mode.
11
+ *
12
+ * A combat technique is authored as a `skill` of subtype `combattechnique` —
13
+ * the standalone item type was merged into Skill — and carries an embedded,
14
+ * discriminated strike mode. It is mandatory for that subtype and absent from
15
+ * every other skill, which is a conditional a flat field list cannot state, so
16
+ * it is applied after the declaration runs.
17
+ *
18
+ * @type {import("../engine/field-spec.mjs").FieldSpec}
19
+ */
20
+ export const COMBAT_TECHNIQUE_STRIKE_MODE: import("../engine/field-spec.mjs").FieldSpec;
@@ -0,0 +1,19 @@
1
+ export class Items extends BasePackCompiler {
2
+ /**
3
+ * How many of each item type this pass wrote, for the summary. Every type
4
+ * is present from the start so the tally reads as a census of the
5
+ * whitelist rather than of what happened to compile.
6
+ *
7
+ * @type {Record<string, number>}
8
+ */
9
+ counts: Record<string, number>;
10
+ /** An item is named by its own type in the log, not by "item". */
11
+ noteLabel(fm: any): any;
12
+ /** @inheritdoc */
13
+ onCompiled(fm: any): void;
14
+ /** @inheritdoc */
15
+ reportCompiled(stats: any): void;
16
+ /** @inheritdoc */
17
+ reportDetail(stats: any): void;
18
+ }
19
+ import { BasePackCompiler } from "../engine/base-compiler.mjs";
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Resolves inline TypeDoc `{@link}` / `{@linkcode}` / `{@linkplain}` tags
3
+ * against the API symbol map.
4
+ *
5
+ * - A known target becomes a Markdown link to its page on the API site;
6
+ * `@linkcode` renders the text as code.
7
+ * - Optional display text (`{@link Target | text}` or `{@link Target text}`)
8
+ * wins; otherwise the last dotted segment is the link text.
9
+ * - An external URL target becomes a plain Markdown link.
10
+ * - Anything the map does not know — `{@link Symbol.member}` written as a syntax
11
+ * example, say — degrades to a code span. Never a broken link, never a build
12
+ * failure.
13
+ *
14
+ * @param {string} body - The markdown body.
15
+ * @param {Record<string, string>} symbols - The symbol map.
16
+ * @param {string} apiBase - Where the generated API documentation is served.
17
+ * @returns {string} The body with every tag resolved.
18
+ */
19
+ export function resolveApiLinks(body: string, symbols: Record<string, string>, apiBase: string): string;
20
+ /**
21
+ * Rewrites the relative links in a developer-doc body so they resolve on the
22
+ * published site.
23
+ *
24
+ * Developer docs are authored to link one another and the source tree with
25
+ * repository-relative paths, and neither target exists at the same path once
26
+ * rendered. Each link is resolved against the doc's own location:
27
+ *
28
+ * - a `*.md` link landing inside the documentation tree becomes the published
29
+ * route, preserving any `#anchor`; a `README` is its directory's landing, so
30
+ * that segment is dropped.
31
+ * - anything else — source, templates, a repository-root `*.md` — becomes its
32
+ * GitHub blob URL.
33
+ *
34
+ * Absolute URLs, anchor-only links, `mailto:` and site-root links are untouched.
35
+ *
36
+ * @param {string} body - The markdown body.
37
+ * @param {string} docRel - The doc's path relative to the documentation tree.
38
+ * @param {object} options - `{ repoRoot, docsSrc, docsRel, route, blob }`.
39
+ * @returns {string} The body with every relative link rewritten.
40
+ */
41
+ export function rewriteRepoLinks(body: string, docRel: string, options: object): string;
42
+ /**
43
+ * The `sohl` knowledgebase pass bundle, built from its options.
44
+ *
45
+ * A pass bundle is two optional hooks the page renderer calls around wikilink
46
+ * resolution, and the order matters:
47
+ *
48
+ * - `beforeLinks` runs on every page, before wikilinks resolve, because a
49
+ * `{@link}` tag may sit inside prose a wikilink also touches.
50
+ * - `afterLinks` runs only on pages from an **extra tree** — the documentation
51
+ * tree — because repository-relative links are a property of how those pages
52
+ * are authored, not of content notes.
53
+ *
54
+ * Both run inside code-fence protection, so neither can rewrite a fenced example.
55
+ *
56
+ * @param {object} options - Resolved from `site.passOptions`.
57
+ * @param {string} [options.symbolMap] - Path to the TypeDoc symbol map.
58
+ * @param {string} [options.apiBase] - Where the API documentation is served.
59
+ * @param {string} [options.blob] - GitHub blob base for repository files.
60
+ * @param {string} options.repoRoot - The repository root, for relative paths.
61
+ * @returns {{beforeLinks: Function, afterLinks: Function}} The bundle.
62
+ */
63
+ export function sohlKbPass(options: {
64
+ symbolMap?: string | undefined;
65
+ apiBase?: string | undefined;
66
+ blob?: string | undefined;
67
+ repoRoot: string;
68
+ }): {
69
+ beforeLinks: Function;
70
+ afterLinks: Function;
71
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Every content type this package compiles, and what a note of that type may
3
+ * write.
4
+ *
5
+ * @type {Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>}
6
+ */
7
+ export const NOTE_SCHEMAS: Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>;