@heroiclands/package-build 0.6.1 → 3.0.1

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 +68 -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,283 @@
1
+ /**
2
+ * Whether a content note's type compiles into a Scene.
3
+ *
4
+ * @param {string} [type] - The note's `type` frontmatter.
5
+ * @returns {boolean} True for a map type.
6
+ */
7
+ export function isMapType(type?: string): boolean;
8
+ /**
9
+ * The canvas profile for a map type.
10
+ *
11
+ * @param {string} type - The note's `type`.
12
+ * @returns {object} The profile from {@link MAP_TYPE_PROFILES}.
13
+ * @throws {Error} When the type is not a map type — the build's fail-fast
14
+ * contract, so a typo never ships a scene with Foundry's own defaults.
15
+ */
16
+ export function mapProfile(type: string): object;
17
+ /**
18
+ * The id of one region within its scene.
19
+ *
20
+ * Derived rather than stored so a cross-reference (`teleportToken`,
21
+ * `toggleBehavior`) can be resolved to a UUID before the target scene has been
22
+ * compiled. An authored `_id` always wins — the converter pins ids on
23
+ * write-back exactly as it does for items and actors.
24
+ *
25
+ * @param {string} sceneId - The owning scene's `_id`.
26
+ * @param {string} key - The region's authored key.
27
+ * @param {string} [pinned] - An authored `_id`, if any.
28
+ * @returns {string} A 16-character Foundry id.
29
+ */
30
+ export function regionDocId(sceneId: string, key: string, pinned?: string): string;
31
+ /**
32
+ * The id of one behaviour within its region.
33
+ *
34
+ * @param {string} regionId - The owning region's `_id`.
35
+ * @param {string} key - The behaviour's authored key.
36
+ * @param {string} [pinned] - An authored `_id`, if any.
37
+ * @returns {string} A 16-character Foundry id.
38
+ */
39
+ export function behaviorDocId(regionId: string, key: string, pinned?: string): string;
40
+ /**
41
+ * A region's highlight colour, derived from its key.
42
+ *
43
+ * Foundry's default is `Color.fromHSV([Math.random(), 0.8, 0.8])` — a different
44
+ * value on every create, which would make each build's output differ from the
45
+ * last for no reason anyone could see. Hashing the key keeps builds
46
+ * reproducible and diffs quiet, in the same hue/saturation family Foundry
47
+ * itself would have chosen, and keeps the value out of the note entirely.
48
+ *
49
+ * @param {string} key - The region's authored key.
50
+ * @returns {string} A CSS hex colour.
51
+ */
52
+ export function regionColor(key: string): string;
53
+ /**
54
+ * The map's own measurements, from which both unit lints are derived.
55
+ *
56
+ * @typedef {object} MapGeometry
57
+ * @property {string} label - The authored key, for error messages.
58
+ * @property {number} pxPerGrid - Pixels per grid square.
59
+ * @property {number[]} dimensions - `[width, height]` in pixels.
60
+ */
61
+ /**
62
+ * Reject geometry authored in grid squares where pixels belong.
63
+ *
64
+ * A grid-valued wall or region is not rejected by Foundry: it becomes a
65
+ * 12x11-pixel feature in the top-left corner, invisible on the map and
66
+ * impossible to notice in a diff. The test is derived from the map itself
67
+ * rather than a magic number — when every coordinate of one feature is smaller
68
+ * than a single grid square, on a map several squares across, the author
69
+ * counted squares.
70
+ *
71
+ * @param {number[]} coords - The feature's coordinates, in pixels.
72
+ * @param {MapGeometry} geom - The map's measurements.
73
+ * @throws {Error} When the coordinates read as grid units.
74
+ */
75
+ export function assertPixelGeometry(coords: number[], geom: MapGeometry): void;
76
+ /**
77
+ * Reject a map pin authored in pixels where grid squares belong.
78
+ *
79
+ * @param {number[]} at - The pin's `[x, y]`, in grid squares.
80
+ * @param {MapGeometry} geom - The map's measurements.
81
+ * @throws {Error} When the coordinates read as pixels.
82
+ */
83
+ export function assertGridLocation(at: number[], geom: MapGeometry): void;
84
+ /**
85
+ * Compile a wall's `blocks:` / `limits:` lists into Foundry's four numeric
86
+ * restriction fields.
87
+ *
88
+ * The authored vocabulary is deliberately not Foundry's.
89
+ * `WALL_MOVEMENT_TYPES.NONE` means movement does **not** collide — i.e.
90
+ * passable — so `movement: none` reads as the exact opposite of what it does.
91
+ * `blocks:` says what the wall stops and `limits:` what it merely attenuates;
92
+ * anything unnamed is passable.
93
+ *
94
+ * @param {{blocks?: string[], limits?: string[]}} spec - The authored lists.
95
+ * @param {string} label - The authored key, for error messages.
96
+ * @returns {{move: number, sight: number, light: number, sound: number}} The
97
+ * Wall restriction fields.
98
+ * @throws {Error} On an unknown sense, a sense in both lists, or a *limited*
99
+ * movement restriction (movement is binary — it has no LIMITED value).
100
+ */
101
+ export function wallRestrictions(spec: {
102
+ blocks?: string[];
103
+ limits?: string[];
104
+ }, label: string): {
105
+ move: number;
106
+ sight: number;
107
+ light: number;
108
+ sound: number;
109
+ };
110
+ /**
111
+ * Compile one authored shape into a Foundry shape record.
112
+ *
113
+ * Four forms only — `rectangle` (`rect` is accepted as its short spelling),
114
+ * `circle`, `ellipse` and `polygon`. The remaining six Foundry shapes are
115
+ * template and token-attached forms with no place in an authored map, and
116
+ * `gridBased` is deliberately not exposed: it re-interprets a shape metrically,
117
+ * and one unit convention per note is the whole point.
118
+ *
119
+ * @param {object} spec - The authored shape entry.
120
+ * @param {MapGeometry} geom - The map's measurements.
121
+ * @returns {object} The Foundry shape record.
122
+ * @throws {Error} On an unknown form, a degenerate polygon, or grid-unit
123
+ * coordinates.
124
+ */
125
+ export function buildShape(spec: object, geom: MapGeometry): object;
126
+ /**
127
+ * Compile a map note into a Scene document, embedded documents and all.
128
+ *
129
+ * Every embedded document carries its own `_key`. This is not decoration: the
130
+ * compendium CLI writes each one straight to its sublevel by that key, and a
131
+ * missing one fails the compile with `Key cannot be null or undefined`.
132
+ *
133
+ * @param {object} fm - The note's frontmatter.
134
+ * @param {object} ctx - The compile context:
135
+ * `packageId`, `journalEntryId`, `journalPack` (the pack the note's derived
136
+ * JournalEntry landed in), `pageIds` (heading key → page id),
137
+ * `resolveRegionRef` / `resolveBehaviorRef` / `resolveEffectRef` (address →
138
+ * UUID), `knownActions`, `warnings`, and optionally `folder` and `stats`.
139
+ * @returns {object} The Scene document, keyed for the pack.
140
+ * @throws {Error} On any authoring mistake Foundry would accept silently.
141
+ */
142
+ export function buildScene(fm: object, ctx: object): object;
143
+ /**
144
+ * Synthesise the scene's single embedded Level from `image:` / `overlay:`.
145
+ *
146
+ * Authors never write `levels:`. A scene must ship at least one Level — the
147
+ * client-side `_preCreate` net that would create one does not run for offline
148
+ * pack compilation, and the server-side migration shim is version-gated on
149
+ * `_stats.coreVersion`, so a pack stamped 14.x+ skips it and ships a scene with
150
+ * no Level and no map.
151
+ *
152
+ * @param {object} sohl - The note's `sohl:` block.
153
+ * @param {string} sceneId - The owning scene's `_id`.
154
+ * @returns {object} The Level document, keyed for the pack.
155
+ */
156
+ export function buildLevel(sohl: object, sceneId: string): object;
157
+ /**
158
+ * Compile the `walls:` and `doors:` blocks into Wall documents.
159
+ *
160
+ * Walls are keyed by **feature** rather than by restriction signature, so a
161
+ * diff reads "the hayloft walls changed" instead of "line 143 changed", and a
162
+ * human can find the one wall they meant to move.
163
+ *
164
+ * @param {object} sohl - The note's `sohl:` block.
165
+ * @param {MapGeometry} geom - The map's measurements.
166
+ * @param {object} ctx - The compile context.
167
+ * @returns {object[]} The Wall documents.
168
+ */
169
+ export function buildWalls(sohl: object, geom: MapGeometry, ctx: object): object[];
170
+ /**
171
+ * Compile the `lights:` block into AmbientLight documents.
172
+ *
173
+ * `dim` and `bright` are radii in the scene's **distance units** (Foundry's own
174
+ * meaning), not pixels; only `position:` is pixels.
175
+ *
176
+ * @param {object} sohl - The note's `sohl:` block.
177
+ * @param {MapGeometry} geom - The map's measurements.
178
+ * @param {object} ctx - The compile context.
179
+ * @returns {object[]} The AmbientLight documents.
180
+ */
181
+ export function buildLights(sohl: object, geom: MapGeometry, ctx: object): object[];
182
+ /**
183
+ * Compile the `tiles:` block into Tile documents.
184
+ *
185
+ * @param {object} sohl - The note's `sohl:` block.
186
+ * @param {MapGeometry} geom - The map's measurements.
187
+ * @param {object} ctx - The compile context.
188
+ * @returns {object[]} The Tile documents.
189
+ */
190
+ export function buildTiles(sohl: object, geom: MapGeometry, ctx: object): object[];
191
+ /**
192
+ * Compile the `sounds:` block into AmbientSound documents.
193
+ *
194
+ * Positional audio is the one audio mechanism that packages cleanly: `path` is
195
+ * a plain `FilePathField` with no document reference behind it.
196
+ *
197
+ * @param {object} sohl - The note's `sohl:` block.
198
+ * @param {MapGeometry} geom - The map's measurements.
199
+ * @param {object} ctx - The compile context.
200
+ * @returns {object[]} The AmbientSound documents.
201
+ */
202
+ export function buildSounds(sohl: object, geom: MapGeometry, ctx: object): object[];
203
+ /**
204
+ * Compile the `locations:` block into Note documents — the map pins.
205
+ *
206
+ * A location's key names a heading in the note's body, and the pin points at
207
+ * the journal page that heading compiled into. `Note.entryId` / `pageId` are
208
+ * `{idOnly: true}` fields, which return before the compendium guard, so pin
209
+ * targets survive packing intact — but `Note#entry` resolves by bare id against
210
+ * the **world** collection, which is why a map with pins ships inside an
211
+ * Adventure (`keepId: true`).
212
+ *
213
+ * @param {object} sohl - The note's `sohl:` block.
214
+ * @param {MapGeometry} geom - The map's measurements.
215
+ * @param {object} ctx - The compile context.
216
+ * @returns {object[]} The Note documents.
217
+ */
218
+ export function buildLocations(sohl: object, geom: MapGeometry, ctx: object): object[];
219
+ /**
220
+ * Compile the `regions:` block into Region documents with their behaviours.
221
+ *
222
+ * @param {object} sohl - The note's `sohl:` block.
223
+ * @param {MapGeometry} geom - The map's measurements.
224
+ * @param {object} ctx - The compile context.
225
+ * @returns {object[]} The Region documents.
226
+ */
227
+ export function buildRegions(sohl: object, geom: MapGeometry, ctx: object): object[];
228
+ export { MAP_TYPES };
229
+ /**
230
+ * Per-type canvas defaults, emitted **explicitly** on every scene.
231
+ *
232
+ * This is not a convenience. `grid.type`, `grid.distance` and `grid.units` all
233
+ * declare `initial: () => game.system.grid.*`, and there is no `game` at build
234
+ * time — left to their initial they would throw or ship undefined. The numeric
235
+ * values are `CONST.GRID_TYPES` and `CONST.FOG_EXPLORATION_MODES`, spelled out
236
+ * here because the constants module is Foundry's, not ours.
237
+ *
238
+ * @type {Readonly<Record<string, object>>}
239
+ */
240
+ export const MAP_TYPE_PROFILES: Readonly<Record<string, object>>;
241
+ /**
242
+ * Foundry's own id for the level a scene is created with
243
+ * (`Scene.metadata.defaultLevelId`). Adopting it makes every reference to the
244
+ * synthesised level derivable, and matches what Foundry would have produced.
245
+ */
246
+ export const DEFAULT_LEVEL_ID: "defaultLevel0000";
247
+ /**
248
+ * The behaviour types a map note may carry (issue #1525, v1).
249
+ *
250
+ * @type {ReadonlySet<string>}
251
+ */
252
+ export const REGION_BEHAVIOR_TYPES: ReadonlySet<string>;
253
+ /**
254
+ * Behaviour types a map note may **never** carry, and why.
255
+ *
256
+ * `executeScript` is banned outright: its `source` is a `JavaScriptField`, so a
257
+ * note carrying one would compile data into code — the system's top security
258
+ * constraint (non-negotiable rule 10). There is no escape hatch and no
259
+ * configuration that re-enables it. `executeMacro` is merely deferred: its
260
+ * target Macro has to ship in the same Adventure to resolve, and
261
+ * Adventure-bundled macros are not built yet.
262
+ *
263
+ * @type {ReadonlyMap<string, string>}
264
+ */
265
+ export const BANNED_REGION_BEHAVIOR_TYPES: ReadonlyMap<string, string>;
266
+ /**
267
+ * The map's own measurements, from which both unit lints are derived.
268
+ */
269
+ export type MapGeometry = {
270
+ /**
271
+ * - The authored key, for error messages.
272
+ */
273
+ label: string;
274
+ /**
275
+ * - Pixels per grid square.
276
+ */
277
+ pxPerGrid: number;
278
+ /**
279
+ * - `[width, height]` in pixels.
280
+ */
281
+ dimensions: number[];
282
+ };
283
+ import { MAP_TYPES } from "./ids.mjs";
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The nearest configuration file at or above a directory.
3
+ *
4
+ * **Two of them in one directory is an error.** Picking one by precedence would
5
+ * mean a repository mid-conversion silently builds from the file its author is
6
+ * no longer editing, and the build would look entirely healthy while doing it.
7
+ * The walk continues past a directory holding none, so a repository may still
8
+ * sit inside one that has its own.
9
+ *
10
+ * @param {string} from - The directory to start from.
11
+ * @returns {string|undefined} Its absolute path, or `undefined` if the walk
12
+ * reaches the filesystem root without finding one.
13
+ * @throws {Error} When one directory holds more than one of
14
+ * {@link CONFIG_FILENAMES}.
15
+ */
16
+ export function findConfigFile(from: string): string | undefined;
17
+ /**
18
+ * Turn a parsed YAML configuration into the frozen one the engine reads.
19
+ *
20
+ * Three fields exist in a code configuration only because a code file has to
21
+ * *compute* what a data file can simply be told, and each is derived here from
22
+ * where the file sits:
23
+ *
24
+ * - **`rootDir`** is the configuration's own directory, always. A data file
25
+ * cannot write `import.meta.dirname`, and any absolute path it wrote instead
26
+ * would be one machine's — so authoring it is rejected rather than honoured.
27
+ * - **`itemBuilders`** is a *name* (`sohl`), resolved against the built-in
28
+ * registries. A registry of a consumer's own is code, and code goes in an
29
+ * `.mjs` configuration.
30
+ * - **`stats.systemVersion`** is derived from the adjacent `package.json` when
31
+ * the configuration does not state it. Stating it is still allowed: a
32
+ * repository shipping content *for* another package (a module declaring
33
+ * `systemId: sohl`) may need a version that is not its own.
34
+ *
35
+ * Everything else passes through untouched, to be validated by the one
36
+ * validator both configuration forms end at.
37
+ *
38
+ * @param {unknown} data - The parsed configuration document.
39
+ * @param {string} configPath - Absolute path of the file it was parsed from.
40
+ * @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
41
+ * @throws {Error} When the document is not a mapping, declares `rootDir`, or
42
+ * names an item-builder registry this package does not ship.
43
+ */
44
+ export function configFromData(data: unknown, configPath: string): import("../config.mjs").ContentBuildConfig;
45
+ /**
46
+ * The consuming repository's resolved, frozen configuration.
47
+ *
48
+ * Every engine module that needs a configured value calls this, rather than
49
+ * hoisting one at import: that is what keeps the library importable without a
50
+ * configuration (#2). The result is memoised, so calling it in a default
51
+ * parameter — the usual spelling here — costs one property read per call.
52
+ *
53
+ * @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
54
+ * @throws {Error} When no configuration file can be found, or the one named
55
+ * cannot be loaded. Absence is a defect, not a fallback: without it the
56
+ * compilers know neither what to compile nor where to put it.
57
+ */
58
+ export function loadPackConfig(): import("../config.mjs").ContentBuildConfig;
59
+ /** The stem every consuming repository declares its build under. */
60
+ export const CONFIG_BASENAME: "package-build.config";
61
+ /**
62
+ * The file names a configuration may be written as, in resolution order.
63
+ *
64
+ * Order settles nothing in practice — two of these in one directory is an
65
+ * error, not a precedence question (see {@link findConfigFile}) — but it fixes
66
+ * the order the names are *reported* in, which is the order a reader should
67
+ * reach for them: YAML first, `.mjs` last.
68
+ *
69
+ * The `content-build.config.*` stem this package read before the two toolchains
70
+ * merged is **not** resolved. 3.0.0 renames the file rather than accepting both:
71
+ * a deprecation window here would mean a repository could sit indefinitely on a
72
+ * name for a package that no longer exists, and the upgrade already requires
73
+ * touching the consumer's manifest and imports.
74
+ *
75
+ * @type {readonly string[]}
76
+ */
77
+ export const CONFIG_FILENAMES: readonly string[];
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Build the router for one configured pack list.
3
+ *
4
+ * Pure — it reads the list and nothing else, so a consumer's routing can be
5
+ * tested without a content tree or a config file on disk.
6
+ *
7
+ * @param {readonly object[]} packs - The resolved `packs` list from
8
+ * `defineConfig`.
9
+ * @returns {{resolve: (fm: object, docType: string) => string,
10
+ * resolveOrNull: (fm: object, docType: string) => string|undefined,
11
+ * packsOfType: (docType: string) => string[],
12
+ * defaultOf: (docType: string) => string|undefined}} The router.
13
+ */
14
+ export function createPackRouter(packs: readonly object[]): {
15
+ resolve: (fm: object, docType: string) => string;
16
+ resolveOrNull: (fm: object, docType: string) => string | undefined;
17
+ packsOfType: (docType: string) => string[];
18
+ defaultOf: (docType: string) => string | undefined;
19
+ };
20
+ /**
21
+ * The router for a resolved configuration, built once per configuration.
22
+ *
23
+ * @param {object} config - A configuration from `defineConfig`.
24
+ * @returns {ReturnType<typeof createPackRouter>} Its router.
25
+ */
26
+ export function routerFor(config: object): ReturnType<typeof createPackRouter>;
27
+ /**
28
+ * The consuming repository's own router — what every module that emits a UUID
29
+ * asks where a note's document lives.
30
+ *
31
+ * An accessor rather than a hoisted constant, so that importing this module
32
+ * needs no configuration (#2). {@link routerFor} keeps one router per
33
+ * configuration object, so repeated calls return the same instance.
34
+ *
35
+ * @returns {ReturnType<typeof createPackRouter>} This repository's router.
36
+ */
37
+ export function packRouter(): ReturnType<typeof createPackRouter>;
38
+ /**
39
+ * A note that cannot be routed to a pack. Thrown rather than returned so no
40
+ * caller can carry on with a plausible-looking default.
41
+ */
42
+ export class PackRoutingError extends Error {
43
+ /** @param {string} message */
44
+ constructor(message: string);
45
+ }
46
+ /**
47
+ * The frontmatter field a note declares its pack in.
48
+ *
49
+ * Deliberately close to `package:` and deliberately not the same word: a note's
50
+ * `package:` says which *distribution* owns it, `pack:` which *compendium*
51
+ * receives its document.
52
+ */
53
+ export const PACK_FIELD: "pack";
@@ -0,0 +1,121 @@
1
+ /**
2
+ * The shared options for one file, with the per-language adjustment applied.
3
+ *
4
+ * What a consumer's own Prettier config would have produced, for a repository
5
+ * that declares none.
6
+ *
7
+ * @param {string} file - Path of the file about to be formatted.
8
+ * @returns {object} Options to hand Prettier directly. Never carries
9
+ * `overrides`: passing that inline is what silently did nothing (#76).
10
+ */
11
+ export function sharedPrettierOptionsFor(file: string): object;
12
+ /**
13
+ * The prose conventions every content repository writes to — one Prettier
14
+ * configuration and one markdownlint rule set, declared here so a note
15
+ * formatted in one repository is formatted the same way in the next (#69).
16
+ *
17
+ * These used to exist in exactly one consumer. The SoHL repository carried
18
+ * both; `sohl-thalorna` had Prettier but never ran it from `lint`; and
19
+ * `sohl-kethira-basic` had neither, so the package least likely to have been
20
+ * proofread was checked for addresses and nothing else. A rule set copied into
21
+ * three repositories is one rule with three implementations, which is the drift
22
+ * #20 exists to remove — so it is declared once, here, and every consumer
23
+ * invokes it.
24
+ *
25
+ * **Neither of these is an override.** A consumer that declares its own
26
+ * Prettier config or its own `.markdownlint-cli2.jsonc` wins; what ships here
27
+ * is the default a repository gets for declaring nothing. Repository *layout*
28
+ * knowledge — which paths to skip — stays with the repository that has the
29
+ * layout, in its own ignore files.
30
+ *
31
+ * @module
32
+ */
33
+ /**
34
+ * The Prettier options every content repository shares, before any per-language
35
+ * adjustment.
36
+ *
37
+ * The values are not arbitrary: they are the ones the SoHL repository has
38
+ * always used, kept identical here so a module or a note moving between
39
+ * repositories does not reformat on arrival. Changing one of these reformats
40
+ * every consumer, so treat it as a breaking change to the shared tree rather
41
+ * than a preference.
42
+ *
43
+ * @type {Readonly<object>}
44
+ */
45
+ export const PRETTIER_BASE: Readonly<object>;
46
+ /**
47
+ * What markdown gets on top of {@link PRETTIER_BASE}.
48
+ *
49
+ * Markdown indents at 2, not the global 4. Notes are the thing several
50
+ * repositories exchange, so their indentation is the one value that most needs
51
+ * to be the same everywhere — a note's YAML frontmatter is nested lists, and at
52
+ * 4 every note reindents away from the form it was written in.
53
+ *
54
+ * **Declared apart from the `overrides` block, not derived from it.** Prettier
55
+ * applies `overrides` only while resolving a config *file*; options handed to
56
+ * it directly keep the global values, so a consumer with no config of its own
57
+ * silently got markdown at 4 (#76). The runner needs the adjustment as data it
58
+ * can apply itself, and {@link PRETTIER_CONFIG} composes the same values into
59
+ * the shape a config file wants — one source, two presentations.
60
+ *
61
+ * @type {Readonly<object>}
62
+ */
63
+ export const PRETTIER_MARKDOWN: Readonly<object>;
64
+ /**
65
+ * The shared configuration in the shape a Prettier **config file** takes.
66
+ *
67
+ * This is what a consumer's `prettier.config.mjs` re-exports, and it is the
68
+ * form in which the markdown adjustment works: resolved from the consumer's own
69
+ * root, `**\/*.md` matches that repository's markdown. Shipped from inside
70
+ * `node_modules` it would match nothing, because Prettier resolves an
71
+ * override's glob relative to the config file's own directory — which is why
72
+ * the runner applies {@link PRETTIER_MARKDOWN} itself rather than pointing
73
+ * Prettier at this file.
74
+ *
75
+ * @type {Readonly<object>}
76
+ */
77
+ export const PRETTIER_CONFIG: Readonly<object>;
78
+ /**
79
+ * The markdownlint rules — the structural checks Prettier cannot make.
80
+ *
81
+ * Prettier already formats every hand-written `.md` file, and it is indifferent
82
+ * to structure: it will happily reformat a document whose heading levels skip
83
+ * from h3 to h5, whose two sibling sections claim the same anchor, or whose
84
+ * link is `(text)[url]`. Those are the defects this set is for.
85
+ *
86
+ * **THE RULE SET IS DELIBERATELY NARROW, and stays that way on purpose.**
87
+ * Turning on markdownlint's defaults over a content tree produces tens of
88
+ * thousands of findings, almost all of them line length, list indentation and
89
+ * blank-line placement — Prettier's territory, or a second formatter's taste
90
+ * imposed on prose it already owns. So `default` is off and each rule below is
91
+ * enabled by name, with the reason it earns its place. Add a rule only if it
92
+ * can report that a page is *wrong*.
93
+ *
94
+ * @type {Readonly<object>}
95
+ */
96
+ export const MARKDOWNLINT_CONFIG: Readonly<object>;
97
+ /**
98
+ * The globs `content-build markdown` checks when a consumer names no paths.
99
+ *
100
+ * Every markdown file the repository tracks, which is what a consumer means by
101
+ * "lint my markdown". What to *skip* is the repository's own business and comes
102
+ * from its `.gitignore` (honoured by default) and its own configuration.
103
+ *
104
+ * @type {readonly string[]}
105
+ */
106
+ export const MARKDOWN_GLOBS: readonly string[];
107
+ /**
108
+ * Markdown every consumer has and nobody hand-writes.
109
+ *
110
+ * `CHANGELOG.md` is assembled by `changeset version` from the `.changeset/*.md`
111
+ * bodies on every release. It is not in anyone's `.gitignore` — it is
112
+ * committed — so nothing else excludes it, and linting it reports on the
113
+ * generator: the findings are real (a heading level changesets chose, an
114
+ * asterisk it emitted) and unfixable, because the next release rewrites the
115
+ * file. Every repository here releases through changesets, so this is a fact
116
+ * about the shared toolchain rather than any one repository's layout, and it
117
+ * belongs in the default instead of being rediscovered three times.
118
+ *
119
+ * @type {readonly string[]}
120
+ */
121
+ export const MARKDOWN_IGNORES: readonly string[];
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Check — or rewrite — every file Prettier claims, under one root.
3
+ *
4
+ * The configuration resolution is the part that matters: a consumer's own
5
+ * Prettier config, found by Prettier walking up from each file, always wins.
6
+ * {@link sharedPrettierOptionsFor} is what a file gets when that search finds
7
+ * nothing, which is the case in a repository that has deliberately declared
8
+ * none.
9
+ *
10
+ * @param {string} root - Repository (or subtree) to check.
11
+ * @param {object} [opts]
12
+ * @param {readonly string[]} [opts.paths] - Files or directories to check
13
+ * instead of the whole root.
14
+ * @param {boolean} [opts.write=false] - Rewrite unformatted files in place
15
+ * rather than reporting them.
16
+ * @param {object} [opts.prettier] - The Prettier module, for tests.
17
+ * @returns {Promise<{findings: Array<{file: string, severity: string,
18
+ * message: string}>, checked: number, written: string[]}>} The findings, how
19
+ * many files were considered, and what was rewritten.
20
+ */
21
+ export function checkFormatting(root: string, opts?: {
22
+ paths?: readonly string[] | undefined;
23
+ write?: boolean | undefined;
24
+ prettier?: object | undefined;
25
+ }): Promise<{
26
+ findings: Array<{
27
+ file: string;
28
+ severity: string;
29
+ message: string;
30
+ }>;
31
+ checked: number;
32
+ written: string[];
33
+ }>;
34
+ /**
35
+ * Lint a repository's markdown against the shared rule set.
36
+ *
37
+ * {@link MARKDOWNLINT_CONFIG} is passed as markdownlint's `optionsDefault`,
38
+ * which is precisely the "shipped default, consumer overrides" behaviour the
39
+ * command promises: a `.markdownlint-cli2.jsonc` found in the tree replaces it,
40
+ * and a repository with none gets these rules.
41
+ *
42
+ * @param {string} root - Repository to lint.
43
+ * @param {object} [opts]
44
+ * @param {readonly string[]} [opts.paths] - Globs to lint instead of every
45
+ * markdown file.
46
+ * @param {boolean} [opts.fix=false] - Apply the fixes markdownlint can make.
47
+ * @param {(params: object) => Promise<number>} [opts.run] - The
48
+ * markdownlint-cli2 entry point, for tests.
49
+ * @returns {Promise<{findings: object[], exitCode: number}>} The findings, and
50
+ * markdownlint's own exit code.
51
+ */
52
+ export function lintMarkdown(root: string, opts?: {
53
+ paths?: readonly string[] | undefined;
54
+ fix?: boolean | undefined;
55
+ run?: ((params: object) => Promise<number>) | undefined;
56
+ }): Promise<{
57
+ findings: object[];
58
+ exitCode: number;
59
+ }>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The curated Foundry region-event vocabulary, as plain data.
3
+ *
4
+ * Deliberately **plain ESM** — no TypeScript, no `@src` aliases, no Foundry —
5
+ * for the same reason `../sohl/default-item-art.mjs` is: the map-note pack
6
+ * compiler runs under bare `node`, outside the bundler that resolves `@src` and
7
+ * strips types, and it must reject an authored region event that the runtime
8
+ * would silently drop. One list here is what keeps the build-time lint and the
9
+ * runtime bridge from drifting apart.
10
+ *
11
+ * It sits in this package rather than in the system's `src/` tree because the
12
+ * map-note compiler that reads it is installed as a dependency (#1501), and a
13
+ * relative path out of the package would resolve to garbage from
14
+ * `node_modules`. The runtime reaches it back through the package's
15
+ * `./engine/region-events` entry point (#1510). It is engine-side, not
16
+ * SoHL-side, because any content module that authors a scene region — an
17
+ * adventure module included — needs this vocabulary.
18
+ *
19
+ * The prose explaining *why* each event is curated or excluded lives with the
20
+ * typed re-exports in `src/entity/event/region-triggers.ts`, which is the
21
+ * documented surface.
22
+ */
23
+ /**
24
+ * The curated Foundry region-event → SoHL trigger-name map. The keys are the
25
+ * `CONST.REGION_EVENTS` string values SoHL forwards.
26
+ *
27
+ * @type {Readonly<Record<string, string>>}
28
+ */
29
+ export const REGION_EVENT_TO_TRIGGER: Readonly<Record<string, string>>;
30
+ /**
31
+ * The Foundry region-event names SoHL forwards (the keys of the map).
32
+ *
33
+ * @type {readonly string[]}
34
+ */
35
+ export const CURATED_REGION_EVENTS: readonly string[];
36
+ /**
37
+ * Region events SoHL deliberately does **not** forward: the continuous
38
+ * (`tokenMove*`), view-dependent (`tokenAnimate*`) and lifecycle
39
+ * (`behavior*`, `regionBoundary`) streams.
40
+ *
41
+ * @type {readonly string[]}
42
+ */
43
+ export const EXCLUDED_REGION_EVENTS: readonly string[];
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The version of `@foundryvtt/foundryvtt-cli` this build actually resolves.
3
+ *
4
+ * Resolved from *this* module, so it names the copy the pack compile runs on
5
+ * rather than whatever a shell happens to find. The installed version is the
6
+ * write path, so it is the fact a report should carry.
7
+ *
8
+ * @returns {string | undefined} The resolved version, or `undefined` when the
9
+ * package cannot be read (a pruned install, an exports-restricted copy).
10
+ */
11
+ export function compendiumCliVersion(): string | undefined;
12
+ /**
13
+ * Every way a compiled pack can ship a Scene that has lost its Level.
14
+ *
15
+ * @param {Iterable<[string, object]>} records - `[key, value]` pairs from a
16
+ * compiled pack's LevelDB, in any order.
17
+ * @param {object} [options] - Reporting context.
18
+ * @param {string} [options.cliVersion] - The resolved compendium CLI version,
19
+ * which decides what a wholesale loss is blamed on. Defaults to unknown,
20
+ * which leaves the report to say only what it can prove.
21
+ * @returns {string[]} One human-readable problem per violation, empty when the
22
+ * pack is sound.
23
+ */
24
+ export function checkSceneLevels(records: Iterable<[string, object]>, { cliVersion }?: {
25
+ cliVersion?: string | undefined;
26
+ }): string[];
27
+ /**
28
+ * Read a compiled pack back off disk and check it.
29
+ *
30
+ * The pack is opened after the compendium CLI has closed it, so this reads the
31
+ * bytes that will actually ship rather than the JSON they were compiled from.
32
+ *
33
+ * @param {string} packDir - Directory of the compiled LevelDB pack.
34
+ * @returns {Promise<string[]>} The problems found, empty when the pack is sound.
35
+ */
36
+ export function verifyPackSceneLevels(packDir: string): Promise<string[]>;