@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,348 @@
1
+ /**
2
+ * Parses a markdown file with YAML frontmatter.
3
+ *
4
+ * Returns `{ frontmatter, body, description, bodyLine, bodyColumn }` where
5
+ * `body` is the trimmed raw markdown after the frontmatter block, and
6
+ * `description` is `body` rendered to HTML. `bodyLine` / `bodyColumn` are the
7
+ * 1-based **file** position of the body's first character, which is what turns
8
+ * an offset within `body` into a position a diagnostic can name (#17) — see
9
+ * {@link positionInBody}. If the file has no frontmatter block, returns
10
+ * `{ frontmatter: null, body: "", description: "" }` with a warn log, and no
11
+ * position: there is no body to have one.
12
+ */
13
+ export function parseMarkdownFile(filePath: any): {
14
+ frontmatter: null;
15
+ body: string;
16
+ description: string;
17
+ bodyLine?: undefined;
18
+ bodyColumn?: undefined;
19
+ } | {
20
+ frontmatter: any;
21
+ body: string;
22
+ description: any;
23
+ bodyLine: number;
24
+ bodyColumn: number;
25
+ };
26
+ /**
27
+ * Recursively yields every `.md` file under `rootDir`, parsed.
28
+ * Yields `{ frontmatter, body, description, file, absPath, bodyLine,
29
+ * bodyColumn }` for each match — the last two from
30
+ * {@link parseMarkdownFile}, so a caller can report a position inside the
31
+ * body as a position in the file (#17).
32
+ * Silently skips directories that don't exist.
33
+ *
34
+ * Directory names in `skipDirectories` are ignored wherever they appear. The
35
+ * walk itself knows nothing about what they mean: `Templates/` is an Obsidian
36
+ * templater convention this repository's vault happens to use, not a property
37
+ * of a content tree, so it is configured rather than hard-coded (#1508).
38
+ *
39
+ * @param {string} rootDir - Root of the tree to walk.
40
+ * @param {object} [opts]
41
+ * @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore.
42
+ * Defaults to the configured list.
43
+ */
44
+ export function walkMarkdownTree(rootDir: string, { skipDirectories }?: {
45
+ skipDirectories?: readonly string[] | undefined;
46
+ }): Generator<{
47
+ file: string & NonSharedBuffer;
48
+ absPath: string;
49
+ frontmatter: null;
50
+ body: string;
51
+ description: string;
52
+ bodyLine?: undefined;
53
+ bodyColumn?: undefined;
54
+ } | {
55
+ file: string & NonSharedBuffer;
56
+ absPath: string;
57
+ frontmatter: any;
58
+ body: string;
59
+ description: any;
60
+ bodyLine: number;
61
+ bodyColumn: number;
62
+ }, void, unknown>;
63
+ /**
64
+ * Resolve the required `sohl.archetype` frontmatter for an Item/Actor entry
65
+ * (see the archetype contract, #604 — `flags.sohl.docArchetype`). The property
66
+ * is a nullable number that authors must state explicitly:
67
+ * - a number → the document is an archetype of that priority.
68
+ * - `null` → the document is not an archetype.
69
+ * - absent → an authoring error (throws), so "not an archetype" is never
70
+ * silently assumed.
71
+ *
72
+ * Reads `sohl.archetype`, falling back to a top-level `archetype` key to match
73
+ * {@link sohlField}'s nested-then-top-level resolution.
74
+ *
75
+ * @param {object} fm Parsed frontmatter.
76
+ * @param {string} label Human-readable context for error messages.
77
+ * @returns {number|undefined} The archetype priority, or `undefined` when null.
78
+ * @throws {Error} When `sohl.archetype` is absent or is not a number/null.
79
+ */
80
+ export function resolveArchetype(fm: object, label: string): number | undefined;
81
+ /**
82
+ * Merge the required `sohl.archetype` frontmatter into a document's `flags`,
83
+ * returning a new object (the input is never mutated). A numeric archetype
84
+ * seeds `flags.sohl.docArchetype`; `null` omits the flag (and clears any stale
85
+ * `docArchetype` while preserving sibling `sohl` flags); an absent value
86
+ * throws. See {@link resolveArchetype}.
87
+ *
88
+ * @param {object} fm Parsed frontmatter.
89
+ * @param {object} [flags] The entry's existing flags (e.g. `fm.flags`).
90
+ * @param {string} label Human-readable context for error messages.
91
+ * @returns {object} The flags object with the archetype applied.
92
+ * @throws {Error} When `sohl.archetype` is absent or invalid.
93
+ */
94
+ export function withArchetypeFlag(fm: object, flags?: object, label: string): object;
95
+ /**
96
+ * Generates a compendium-source filename: `Name_id.json` with non-
97
+ * alphanumeric runs replaced by underscores.
98
+ */
99
+ export function makeFilename(name: any, id: any): string;
100
+ /**
101
+ * Standardize a name into a slug: lowercase, apostrophes removed,
102
+ * non-alphanumerics collapsed to single hyphens.
103
+ */
104
+ /**
105
+ * Translate a content-relative image path into its Foundry-relative form.
106
+ *
107
+ * Content frontmatter (`img` / `portrait`) authors a single path that has to
108
+ * work for Foundry, the knowledgebase, and the website. For Foundry the bundled
109
+ * asset roots — `icons/...` and `images/...` — are served from the package
110
+ * directory, so they are rewritten to `<assetRoot>/<path>` — `systems/sohl/assets`
111
+ * for this repository, `modules/<id>/assets` for a module (#1508). Any other
112
+ * path (already package-rooted, an absolute URL) is returned unchanged, and an
113
+ * empty path yields `""`.
114
+ *
115
+ * This is translation only: the per-type default for an empty result is
116
+ * domain-specific (actors default differently from items, and gear differently
117
+ * again), so each compiler owns its own default and applies it to the result —
118
+ * `resolveImg(fm.img) || <default>`. For items that default is the art paired
119
+ * with the type's builder, reached through `itemArt()`, which runs the path
120
+ * back through this function so a registry entry and a note's `img:` are
121
+ * spelled the same way (#7).
122
+ *
123
+ * @param {string | null | undefined} raw - content-relative path from frontmatter.
124
+ * @param {{assetRoot: string}} [config] - The resolved build configuration.
125
+ * Defaults to this repository's.
126
+ * @returns {string} the Foundry-relative path, or `""` when `raw` is empty.
127
+ */
128
+ export function resolveImg(raw: string | null | undefined, config?: {
129
+ assetRoot: string;
130
+ }): string;
131
+ /**
132
+ * Resolves the display name from frontmatter, preferring `name.full`,
133
+ * falling back to `name` (if string), then `defaultValue`.
134
+ */
135
+ export function resolveName(fm: any, defaultValue?: string): any;
136
+ /**
137
+ * The oldest Foundry core this package supports, stamped into every compiled
138
+ * document as `_stats.coreVersion`.
139
+ *
140
+ * **Derived, never written twice.** `_stats.coreVersion` is what Foundry gates
141
+ * its migration shims on: a record stamped older than a shim is rewritten by it
142
+ * on load. Every pack once shipped `coreVersion: "14"`, which sorts *below*
143
+ * every v14 build and so left all shipped content permanently eligible for
144
+ * every v14 migration (#1533).
145
+ *
146
+ * **Read from the configuration, not from the shipped manifest.** It used to
147
+ * open `paths.packageManifest` and take `compatibility.minimum` out of it,
148
+ * because the configuration had no way to state the fact. Now that it does, the
149
+ * manifest is generated *from* the configuration — so reading it back would be
150
+ * a round trip through an artifact that need not exist yet: `build:db` can run
151
+ * before the manifest is written.
152
+ *
153
+ * Absent is a hard failure, not a default. A guessed floor is stamped into
154
+ * every document in the pack and stays invisible until something migrates on
155
+ * it, which is exactly why the manifest read threw rather than falling back.
156
+ *
157
+ * @param {{compatibility: {minimum: string}|null}} [config] - The resolved
158
+ * configuration. Defaults to this repository's.
159
+ * @returns {string} The declared `compatibility.minimum`.
160
+ * @throws {Error} When the configuration declares no `compatibility.minimum`.
161
+ */
162
+ export function supportedCoreVersion(config?: {
163
+ compatibility: {
164
+ minimum: string;
165
+ } | null;
166
+ }): string;
167
+ /**
168
+ * Default `_stats` block for compiled compendium entries.
169
+ *
170
+ * Every stamped identity is configuration (#1508): four compilers used to pass
171
+ * the same frozen `"0.6.0"` literal, and `systemId` / `lastModifiedBy` were
172
+ * written into this function. `coreVersion` alone is *not* configuration — it
173
+ * comes from {@link supportedCoreVersion}, the configured Foundry floor,
174
+ * so a document never claims to predate the migrations that would rewrite it.
175
+ *
176
+ * @param {string} [systemVersion] - The system version to stamp. Defaults to the
177
+ * configured one.
178
+ * @param {{stats: {systemId: string, systemVersion: string,
179
+ * lastModifiedBy: string}, paths: {packageManifest: string}}} [config] -
180
+ * The resolved build configuration. Defaults to this repository's.
181
+ * @returns {object} The `_stats` block.
182
+ */
183
+ export function buildStats(systemVersion?: string, config?: {
184
+ stats: {
185
+ systemId: string;
186
+ systemVersion: string;
187
+ lastModifiedBy: string;
188
+ };
189
+ paths: {
190
+ packageManifest: string;
191
+ };
192
+ }): object;
193
+ /**
194
+ * The `_stats` block every compiler stamps on an entry it emits, built once.
195
+ *
196
+ * Each compiler used to hoist `const STATS = buildStats()` at module scope,
197
+ * which read the shipped package manifest the moment the module was imported —
198
+ * so importing a compiler required a manifest to exist even when nothing was
199
+ * going to be compiled (#2). Deferred to first use and memoised here, the
200
+ * cost and the identity are what they always were; only the moment moved.
201
+ *
202
+ * @returns {object} The default `_stats` block, shared by every compiler.
203
+ */
204
+ export function defaultStats(): object;
205
+ /**
206
+ * Indexes **every** note in the content tree so any pack compiler can resolve a
207
+ * wikilink to any other document. Shared by all three compilers: a skill links
208
+ * to another skill, a journal to a creature, a creature to a rules page, and
209
+ * each target's own **type** decides which pack the UUID points into.
210
+ *
211
+ * Each note's pack is resolved here, once, and stored on its index entry: a
212
+ * UUID carries a pack name, so a repository shipping several packs of one type
213
+ * (#1566) would otherwise address every one of them as the first. A note whose
214
+ * declaration is unroutable is indexed against the conventional name and left
215
+ * for the compile pass to report — the index has no business failing a build,
216
+ * and the pass fails it with a far better message. The one exception is a
217
+ * **retired** content type (SoHL#1580): this walk is the first to see every
218
+ * note together with its path, and unlike an unroutable declaration there is
219
+ * no pass that would ever claim such a note and report it.
220
+ *
221
+ * @param {string} contentBase - Root of the content tree.
222
+ * @param {object} [router] - The pack router. Supplied by the calling pass so
223
+ * the index and the compile agree about where each note landed; defaults to
224
+ * this repository's own.
225
+ * @returns {{byShortcode: Map, byAlias: Map}} From `buildWikilinkIndex`.
226
+ */
227
+ export function buildContentLinkIndex(contentBase: string, router?: object): {
228
+ byShortcode: Map<any, any>;
229
+ byAlias: Map<any, any>;
230
+ };
231
+ /**
232
+ * Converts the wikilinks in one note's markdown, reporting any that have no
233
+ * target in the content tree. Every compiler funnels through this so the
234
+ * diagnostic text and the leave-it-alone fallback are identical everywhere.
235
+ *
236
+ * Each report names the **file, line and column** the link sits on (#17), so
237
+ * it can be opened and fixed — and so two identical links on one note are
238
+ * tellable apart. That needs `file` and the note's `bodyLine` / `bodyColumn`;
239
+ * without them the diagnostic still reports, one field shorter, rather than
240
+ * inventing a position.
241
+ *
242
+ * @param {string} body - The note's markdown body, tables already expanded.
243
+ * @param {object} ctx - `{ type, id, pack, docPack, index, name }` — `name` is
244
+ * used in the message, and the two pack names address a `[[#slug]]`
245
+ * self-link, whose target is the source note itself and so has no index
246
+ * entry. Position is carried by `{ file, bodyLine, bodyColumn, lineMap }`,
247
+ * the last from {@link expandNoteTables}.
248
+ * @returns {{markdown: string, unresolved: Array<object>}}
249
+ * @throws {Error} On an ambiguous alias or a dead qualified address. The error
250
+ * carries `file` and `position`, so a caller reports it in the same form
251
+ * rather than re-deriving one.
252
+ */
253
+ export function convertNoteWikilinks(body: string, { type, id, pack, docPack, index, name, file, bodyLine, bodyColumn, lineMap, }: object): {
254
+ markdown: string;
255
+ unresolved: Array<object>;
256
+ };
257
+ /**
258
+ * Every note in the content tree, in the shape the `dataview` table expander
259
+ * searches: its frontmatter plus where it sits in the tree. Ordered by path so
260
+ * a table that leaves rows tied still emits identically on every build.
261
+ *
262
+ * @param {string} contentBase - Root of the content tree.
263
+ * @returns {Array<{fm: object, path: string, tld: string, folder: string,
264
+ * absPath: string}>}
265
+ */
266
+ export function collectContentDocs(contentBase: string): Array<{
267
+ fm: object;
268
+ path: string;
269
+ tld: string;
270
+ folder: string;
271
+ absPath: string;
272
+ }>;
273
+ /**
274
+ * Expand the fenced `dataview` tables in one note's markdown, before wikilinks
275
+ * are resolved — so a generated cell may itself be a wikilink.
276
+ *
277
+ * A table searches only notes of the source note's own `package`, so a SoHL
278
+ * page never tabulates setting-package content (and vice versa).
279
+ *
280
+ * @param {string} body - The note's markdown body.
281
+ * @param {object} ctx
282
+ * @param {Array<object>} ctx.docs - From {@link collectContentDocs}.
283
+ * @param {string} ctx.name - The note, for the error message.
284
+ * @param {string} [ctx.pkg] - The source note's `package`.
285
+ * @param {object} [ctx.fm] - The source note's frontmatter, which is what a
286
+ * query's `this` reads. Its entry in `docs` supplies the path as well.
287
+ * @param {number} [ctx.bodyLine] - 1-based file line of the body's first line,
288
+ * so a failing directive can be reported at its position in the file.
289
+ * @returns {{markdown: string, lineMap: Array<{line: number,
290
+ * generated: boolean}>}} The body with every table expanded, and where each
291
+ * emitted line came from — which is what lets a diagnostic about the
292
+ * expanded body name an authored position (#17).
293
+ * @throws {Error} When a query is malformed or unsupported — the note fails to
294
+ * compile rather than shipping a table-shaped hole. The error carries
295
+ * `position`, the directive's own line.
296
+ */
297
+ export function expandNoteTables(body: string, { docs, name, pkg, fm, bodyLine }: {
298
+ docs: Array<object>;
299
+ name: string;
300
+ pkg?: string | undefined;
301
+ fm?: object | undefined;
302
+ bodyLine?: number | undefined;
303
+ }): {
304
+ markdown: string;
305
+ lineMap: Array<{
306
+ line: number;
307
+ generated: boolean;
308
+ }>;
309
+ };
310
+ /**
311
+ * Loads a folders.yaml file as an array of folder entries. Returns []
312
+ * when the file is missing (logging a warning) so packs without folders
313
+ * can opt out simply by not committing the file.
314
+ */
315
+ export function loadFolders(foldersFile: any): any[];
316
+ /**
317
+ * Validates folder invariants and returns a resolver function that maps a
318
+ * folder id to the same id (after verifying it exists). Returns `null` for
319
+ * a null/empty input; throws for an unknown id.
320
+ *
321
+ * Invariants:
322
+ * - Every folder must have a non-empty id
323
+ * - Every folder must have a name
324
+ * - Sibling folders (same parentFolderId) must have unique names
325
+ * - Every parentFolderId must match an existing folder id (or be "")
326
+ *
327
+ * Returns { resolver, folders } where folders is the validated list.
328
+ */
329
+ export function buildFolderResolver(folders: any): {
330
+ resolver: (folderId: any) => string | null;
331
+ folders: any;
332
+ };
333
+ /**
334
+ * Builds a compendium-source filename for a folder JSON document:
335
+ * `folder_Name_id.json` with non-alphanumeric runs replaced by
336
+ * underscores.
337
+ */
338
+ export function folderFilename(name: any, id: any): string;
339
+ /**
340
+ * Writes one JSON document per folder into `destDir`. `documentType`
341
+ * determines the folder's Foundry `type` field — `"Item"` for the items
342
+ * pack, `"JournalEntry"` for the journals pack.
343
+ */
344
+ export function writeFolderDocs(folders: any, stats: any, destDir: any, documentType: any): void;
345
+ export const md: any;
346
+ export { slugify } from "./content-slug.mjs";
347
+ export { makeId } from "./ids.mjs";
348
+ export { getFrontmatter, sohlField, resolveCharges, resolveSkillAptitudes, resolveRelation, requireSubType, parseValueDesc } from "./frontmatter.mjs";
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Stable 16-char hex id derived from `${namespace}:${value}`. Use for deriving
3
+ * page ids from heading text when no explicit id is supplied.
4
+ *
5
+ * @param {string} namespace - Keeps unrelated derivations from colliding.
6
+ * @param {string} value - The input the id is a function of.
7
+ * @returns {string} A 16-character hexadecimal Foundry id.
8
+ */
9
+ export function makeId(namespace: string, value: string): string;
10
+ /**
11
+ * Throw if `type` names a retired content type.
12
+ *
13
+ * @param {string} type - The note's declared `type`, or a link's qualifier.
14
+ * @param {string} [where] - What carries it — a file path, a link target —
15
+ * appended to the message so the reader can go straight to it.
16
+ * @throws {Error} Naming the replacement type.
17
+ */
18
+ export function assertTypeNotRetired(type: string, where?: string): void;
19
+ /**
20
+ * The pack a type's documents live in, in the conventional one-pack-per-type
21
+ * layout.
22
+ *
23
+ * Item types are the open set — a new one is added whenever the system grows a
24
+ * document type — so they are the **default** rather than an enumerated list. A
25
+ * hand-maintained list is what made an entire content directory silently
26
+ * unlinkable once (#1276); nothing to maintain, nothing to forget.
27
+ *
28
+ * The `docType` is the authority: it is a property of the *content type* and
29
+ * holds however a repository names or splits its packs. The `pack` is the
30
+ * conventional name only — a repository may rename its packs, or ship several
31
+ * of one type (#1566), in which case the pack a particular note's document
32
+ * lands in comes from `engine/pack-router.mjs` and is passed to
33
+ * {@link compendiumUuid} explicitly. This module stays free of the
34
+ * configuration so the link resolver above it can stay pure.
35
+ *
36
+ * @param {string} type - The target note's `type`.
37
+ * @returns {{pack: string, docType: string}} The pack and document type.
38
+ * @throws {Error} If `type` names a retired content type — see
39
+ * {@link RETIRED_TYPES}. The open-set default would otherwise route it to the
40
+ * items pack and say nothing.
41
+ */
42
+ export function packForType(type: string): {
43
+ pack: string;
44
+ docType: string;
45
+ };
46
+ /**
47
+ * A document's full compendium UUID.
48
+ *
49
+ * This is the one place a UUID is spelled. Every link is resolved by looking up
50
+ * an address computed here — never by concatenating a prefix at the point of
51
+ * use, which is how the package came to be hard-coded in two separate files.
52
+ *
53
+ * @param {string} packageId - The Foundry package that ships the pack, e.g.
54
+ * `sohl`. A system id or a module id; Foundry addresses both the same way.
55
+ * @param {string} type - The note's content `type`.
56
+ * @param {string} id - The document's id.
57
+ * @param {string} [packName] - The pack the document actually landed in, from
58
+ * the pack router. Supplied wherever the note is known, because a repository
59
+ * may ship several packs of one type and a UUID carries the pack name
60
+ * (#1566). Omitted only where there is no note to route — the conventional
61
+ * name from {@link packForType} then stands in.
62
+ * @returns {string} `Compendium.<packageId>.<pack>.<DocumentType>.<id>`
63
+ */
64
+ export function compendiumUuid(packageId: string, type: string, id: string, packName?: string): string;
65
+ /**
66
+ * The UUID of a JournalEntry page.
67
+ *
68
+ * @param {string} entryUuid - The owning entry's UUID, from
69
+ * {@link compendiumUuid}.
70
+ * @param {string} pageId - The page's id.
71
+ * @returns {string} The page's UUID.
72
+ */
73
+ export function pageUuid(entryUuid: string, pageId: string): string;
74
+ /**
75
+ * Every content type that compiles into a Foundry `Scene` — a **map note**
76
+ * (#1525). The three differ only in derived canvas defaults, which is the map
77
+ * compiler's business; everything else treats them alike.
78
+ *
79
+ * Declared in this leaf module because several passes that must not depend on
80
+ * the map compiler need it: the pack router below, and the doc-carrying type
81
+ * set in `item-docs.mjs` (a map note's prose becomes a JournalEntry, exactly as
82
+ * an item's or a macro's does).
83
+ *
84
+ * @type {ReadonlySet<string>}
85
+ */
86
+ export const MAP_TYPES: ReadonlySet<string>;
87
+ /**
88
+ * Content type → the pack its documents compile into, and the document type
89
+ * that pack holds.
90
+ *
91
+ * These are pack **names**, not addresses. The package that owns the pack is
92
+ * supplied by the caller, because it is a property of the repository doing the
93
+ * building and not of the content: the same notes compiled by a different
94
+ * repository belong to a different package. Baking the package into these
95
+ * values is what made every link emitted by `sohl-thalorna` address the `sohl`
96
+ * system (#1498) — correct here only by coincidence.
97
+ *
98
+ * @type {Readonly<Record<string, {pack: string, docType: string}>>}
99
+ */
100
+ export const PACK_BY_TYPE: Readonly<Record<string, {
101
+ pack: string;
102
+ docType: string;
103
+ }>>;
104
+ /**
105
+ * Content types that no longer exist, and what replaced each one.
106
+ *
107
+ * `character` and `creature` were retired in favour of the single `being` they
108
+ * had always compiled into (SoHL#1580). They are recorded here rather than
109
+ * simply deleted because deleting them is the one change that fails *quietly*:
110
+ * every type not named in {@link PACK_BY_TYPE} falls through to the open item
111
+ * set below, so a note or a link left on the old spelling would be routed to
112
+ * the items pack — a wrong answer, arrived at silently, which is exactly the
113
+ * failure mode the open-set default exists to avoid for real item types.
114
+ *
115
+ * Keeping the names lets {@link assertTypeNotRetired} say what happened and
116
+ * what to write instead. Entries stay for as long as content in the wild might
117
+ * still carry them.
118
+ *
119
+ * @type {Readonly<Record<string, string>>}
120
+ */
121
+ export const RETIRED_TYPES: Readonly<Record<string, string>>;
122
+ /** Where every other content type compiles: the items pack. */
123
+ export const ITEM_PACK: Readonly<{
124
+ pack: "items";
125
+ docType: "Item";
126
+ }>;
@@ -0,0 +1,33 @@
1
+ export * as ids from "./ids.mjs";
2
+ export * as codeFences from "./code-fences.mjs";
3
+ export * as frontmatter from "./frontmatter.mjs";
4
+ export * as contentTree from "./content-tree.mjs";
5
+ export * as packConfig from "./pack-config.mjs";
6
+ export * as packRouter from "./pack-router.mjs";
7
+ export * as contentPackage from "./content-package.mjs";
8
+ export * as contentSlug from "./content-slug.mjs";
9
+ export * as contentAddress from "./content-address.mjs";
10
+ export * as foreignManifests from "./foreign-manifests.mjs";
11
+ export * as kbManifest from "./kb-manifest.mjs";
12
+ export * as manifestEmit from "./manifest-emit.mjs";
13
+ export * as siteBuild from "./site-build.mjs";
14
+ export * as contentLint from "./content-lint.mjs";
15
+ export * as contentLinks from "./content-links.mjs";
16
+ export * as webWikilinks from "./web-wikilinks.mjs";
17
+ export * as contentTables from "./content-tables.mjs";
18
+ export * as helpers from "./helpers.mjs";
19
+ export * as itemRegistry from "./item-registry.mjs";
20
+ export * as itemDocs from "./item-docs.mjs";
21
+ export * as wikilinks from "./wikilinks.mjs";
22
+ export * as wikilinkSyntax from "./wikilink-syntax.mjs";
23
+ export * as siteIndex from "./site-index.mjs";
24
+ export * as baseCompiler from "./base-compiler.mjs";
25
+ export * as journals from "./journals.mjs";
26
+ export * as macros from "./macros.mjs";
27
+ export * as mapNotes from "./map-notes.mjs";
28
+ export * as scenes from "./scenes.mjs";
29
+ export * as sceneLevels from "./scene-levels.mjs";
30
+ export * as generate from "./generate.mjs";
31
+ export * as compendiums from "./compendiums.mjs";
32
+ export * as regionEvents from "./region-events.mjs";
33
+ export { CURATED_REGION_EVENTS, EXCLUDED_REGION_EVENTS, REGION_EVENT_TO_TRIGGER } from "./region-events.mjs";
@@ -0,0 +1,73 @@
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, plus `macro` — a macro note's body documents the script the
6
+ * note also compiles into a Macro (#1514), which is the same shape as an item
7
+ * and its description: one note, two documents, the prose living in the
8
+ * 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 and actors are absent: each is a single document, so it has no
18
+ * separate documentation to address.
19
+ *
20
+ * An accessor rather than a hoisted constant, so that importing this module
21
+ * needs no configuration (#2).
22
+ *
23
+ * @returns {ReadonlySet<string>} The configured doc-carrying types.
24
+ */
25
+ export function docEntryTypes(): ReadonlySet<string>;
26
+ /**
27
+ * Whether a content note's type is one whose prose becomes a JournalEntry of
28
+ * its own.
29
+ *
30
+ * @param {string} type - The note's `type` frontmatter.
31
+ * @returns {boolean} True for an item type, for `macro` and for a map type;
32
+ * false for `doc` and for actors.
33
+ */
34
+ export function hasDocEntry(type: string): boolean;
35
+ /**
36
+ * The id of the JournalEntry a note's prose compiles into — an item's, or a
37
+ * macro's.
38
+ *
39
+ * Derived from the note's own id so that the pass writing the document and the
40
+ * pass writing its documentation agree without either reading the other's
41
+ * output. It is deliberately not the note's id itself: the two documents are
42
+ * distinct, live in different packs, and sharing an id would make either one's
43
+ * UUID ambiguous to read.
44
+ *
45
+ * The `"item-doc"` hash namespace is **frozen**: it is baked into every entry
46
+ * id already shipped, and every `@UUID` pointing at one. It names where the
47
+ * derivation started, not what may use it.
48
+ *
49
+ * @param {string} itemId - The note's `id` frontmatter.
50
+ * @returns {string} A 16-character Foundry id.
51
+ */
52
+ export function itemDocEntryId(itemId: string): string;
53
+ /**
54
+ * The description an item carries in place of its prose: a `@UUID` link to the
55
+ * first page of its item doc, and nothing else.
56
+ *
57
+ * "Nothing else" is the whole convention — a description that is *only* a link
58
+ * is a pointer, and anything alongside it would make it ordinary prose that
59
+ * happens to contain a link, which the runtime would then show verbatim.
60
+ *
61
+ * @param {string} packageId - The Foundry package shipping the journals pack.
62
+ * Supplied rather than assumed (#1498).
63
+ * @param {string} itemId - The item note's `id` frontmatter.
64
+ * @param {string} name - The item's name, used as the link's label. It shows
65
+ * only if the target ever fails to resolve, where a broken link naming the
66
+ * item beats a bare UUID.
67
+ * @param {string} firstPageId - The id of the entry's first page, from
68
+ * {@link journalPageId}.
69
+ * @returns {string} The pointer to store in `system.docHtml`.
70
+ */
71
+ export function itemDocPointer(packageId: string, itemId: string, name: string, firstPageId: string): string;
72
+ export { itemTypes };
73
+ import { itemTypes } from "./item-registry.mjs";
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Every content type that compiles into an item — and therefore into an item
3
+ * doc. Read by the Item compiler to know what to claim, and by the journals
4
+ * pass to know whose prose it is holding.
5
+ *
6
+ * **Derived, never authored.** These are the keys of the consuming
7
+ * repository's `itemBuilders` registry, so the whitelist and the builder table
8
+ * are the same list and cannot drift apart. They already had: `trait` was
9
+ * whitelisted long after the item type was retired (#651), with no builder
10
+ * behind it, so every `type: trait` note passed the gate and then failed to
11
+ * compile (#1504).
12
+ *
13
+ * An accessor rather than a hoisted constant, so that importing this module
14
+ * needs no configuration (#2).
15
+ *
16
+ * @returns {ReadonlySet<string>} The configured item types.
17
+ */
18
+ export function itemTypes(): ReadonlySet<string>;
19
+ /**
20
+ * The builder the consuming repository registered for an item type.
21
+ *
22
+ * Unreachable through the compiler — its whitelist *is* this registry's keys —
23
+ * so a throw here means a caller invented a type. It names the type rather than
24
+ * failing as an anonymous `is not a function` (#1504).
25
+ *
26
+ * @param {string} type - The note's `type` frontmatter.
27
+ * @returns {(fm: object) => object} The builder for that type.
28
+ * @throws {Error} When the configuration registers no builder for `type`.
29
+ */
30
+ export function itemBuilder(type: string): (fm: object) => object;
31
+ /**
32
+ * The default art for an item type — the image a note of that type is given
33
+ * when it carries no `img:` of its own.
34
+ *
35
+ * Read from the consuming repository's `itemBuilders` registry, the same place
36
+ * the type itself is declared, so a consumer's own type can bring art a
37
+ * SoHL-owned table could never hold. Art used to be looked up in
38
+ * `sohl/default-item-art.mjs` instead: a type was configurable while its
39
+ * default art was not, so a second consumer's items compiled only if every one
40
+ * of its notes set `img:` (#7).
41
+ *
42
+ * **Still fail-fast.** A type with neither a note-level `img:` nor paired art
43
+ * aborts the pack build rather than shipping a mismatched icon — the contract
44
+ * `defaultItemArt` was written for. Only the error's *owner* changed: it now
45
+ * names the registry the consumer declares and can add to.
46
+ *
47
+ * **Resolved by the same rule a note's `img:` is.** The path goes through
48
+ * {@link resolveImg}, so `icons/relic.svg` means the consumer's own asset root
49
+ * in the registry exactly as it does on a note, and an already-served path
50
+ * (`systems/sohl/assets/…`, as every SoHL default is) passes through untouched.
51
+ * One spelling, one meaning, wherever it is written.
52
+ *
53
+ * @param {string} type - the item type.
54
+ * @returns {string} The default image path for that type.
55
+ * @throws {Error} When the type's registry entry pairs no `img`.
56
+ */
57
+ export function itemArt(type: string): string;