@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.
- package/CHANGELOG-content-build.md +965 -0
- package/CHANGELOG.md +68 -0
- package/CONTENT.md +824 -0
- package/MIGRATING.md +127 -0
- package/README.md +29 -17
- package/bin/content-build.mjs +1082 -0
- package/bin/package-build.mjs +2 -2
- package/bin/report.mjs +2 -2
- package/config.mjs +21 -18
- package/content-config.mjs +1424 -0
- package/coverage.mjs +1 -1
- package/e2e.mjs +1 -1
- package/engine/abbreviations.mjs +0 -0
- package/engine/base-compiler.mjs +608 -0
- package/engine/code-fences.mjs +311 -0
- package/engine/compendiums.mjs +353 -0
- package/engine/content-address.mjs +141 -0
- package/engine/content-links.mjs +438 -0
- package/engine/content-lint.mjs +204 -0
- package/engine/content-package.mjs +67 -0
- package/engine/content-slug.mjs +169 -0
- package/engine/content-tables.mjs +1216 -0
- package/engine/content-tree.mjs +56 -0
- package/engine/diagnostics.mjs +270 -0
- package/engine/field-reference.mjs +242 -0
- package/engine/field-spec.mjs +218 -0
- package/engine/foreign-catalog.mjs +450 -0
- package/engine/foreign-manifests.mjs +129 -0
- package/engine/frontmatter-lint.mjs +343 -0
- package/engine/frontmatter.mjs +258 -0
- package/engine/generate.mjs +305 -0
- package/engine/helpers.mjs +809 -0
- package/engine/ids.mjs +192 -0
- package/engine/index.mjs +142 -0
- package/engine/item-docs.mjs +153 -0
- package/engine/item-registry.mjs +129 -0
- package/engine/journals.mjs +374 -0
- package/engine/kb-manifest.mjs +473 -0
- package/engine/macros.mjs +333 -0
- package/engine/manifest-emit.mjs +339 -0
- package/engine/map-notes.mjs +1302 -0
- package/engine/pack-config.mjs +479 -0
- package/engine/pack-router.mjs +254 -0
- package/engine/prose-config.mjs +238 -0
- package/engine/prose-lint.mjs +294 -0
- package/engine/region-events.mjs +79 -0
- package/engine/scene-levels.mjs +420 -0
- package/engine/scenes.mjs +528 -0
- package/engine/site-build.mjs +778 -0
- package/engine/site-index.mjs +332 -0
- package/engine/web-wikilinks.mjs +330 -0
- package/engine/wikilink-syntax.mjs +105 -0
- package/engine/wikilinks.mjs +645 -0
- package/index.mjs +42 -25
- package/lang.mjs +1 -1
- package/manifest.mjs +79 -5
- package/markdownlint-config.mjs +45 -0
- package/package.json +78 -35
- package/prettier-config.mjs +34 -0
- package/sohl/actors.mjs +581 -0
- package/sohl/affiliation-standings.mjs +44 -0
- package/sohl/being-info.mjs +173 -0
- package/sohl/default-item-art.mjs +82 -0
- package/sohl/index.mjs +55 -0
- package/sohl/item-builders.mjs +114 -0
- package/sohl/item-fields.mjs +867 -0
- package/sohl/items.mjs +197 -0
- package/sohl/kb-passes.mjs +207 -0
- package/sohl/note-schemas.mjs +322 -0
- package/templates.mjs +1 -1
- package/types/config.d.mts +1 -1
- package/types/content-config.d.mts +571 -0
- package/types/engine/abbreviations.d.mts +44 -0
- package/types/engine/base-compiler.d.mts +320 -0
- package/types/engine/code-fences.d.mts +102 -0
- package/types/engine/compendiums.d.mts +77 -0
- package/types/engine/content-address.d.mts +61 -0
- package/types/engine/content-links.d.mts +87 -0
- package/types/engine/content-lint.d.mts +47 -0
- package/types/engine/content-package.d.mts +36 -0
- package/types/engine/content-slug.d.mts +59 -0
- package/types/engine/content-tables.d.mts +131 -0
- package/types/engine/content-tree.d.mts +12 -0
- package/types/engine/diagnostics.d.mts +152 -0
- package/types/engine/field-reference.d.mts +20 -0
- package/types/engine/field-spec.d.mts +181 -0
- package/types/engine/foreign-catalog.d.mts +103 -0
- package/types/engine/foreign-manifests.d.mts +43 -0
- package/types/engine/frontmatter-lint.d.mts +74 -0
- package/types/engine/frontmatter.d.mts +99 -0
- package/types/engine/generate.d.mts +56 -0
- package/types/engine/helpers.d.mts +348 -0
- package/types/engine/ids.d.mts +126 -0
- package/types/engine/index.d.mts +33 -0
- package/types/engine/item-docs.d.mts +73 -0
- package/types/engine/item-registry.d.mts +57 -0
- package/types/engine/journals.d.mts +121 -0
- package/types/engine/kb-manifest.d.mts +214 -0
- package/types/engine/macros.d.mts +201 -0
- package/types/engine/manifest-emit.d.mts +137 -0
- package/types/engine/map-notes.d.mts +283 -0
- package/types/engine/pack-config.d.mts +77 -0
- package/types/engine/pack-router.d.mts +53 -0
- package/types/engine/prose-config.d.mts +121 -0
- package/types/engine/prose-lint.d.mts +59 -0
- package/types/engine/region-events.d.mts +43 -0
- package/types/engine/scene-levels.d.mts +36 -0
- package/types/engine/scenes.d.mts +48 -0
- package/types/engine/site-build.d.mts +214 -0
- package/types/engine/site-index.d.mts +141 -0
- package/types/engine/web-wikilinks.d.mts +67 -0
- package/types/engine/wikilink-syntax.d.mts +101 -0
- package/types/engine/wikilinks.d.mts +172 -0
- package/types/index.d.mts +3 -0
- package/types/manifest.d.mts +52 -4
- package/types/markdownlint-config.d.mts +8 -0
- package/types/prettier-config.d.mts +1 -0
- package/types/sohl/actors.d.mts +65 -0
- package/types/sohl/affiliation-standings.d.mts +22 -0
- package/types/sohl/being-info.d.mts +78 -0
- package/types/sohl/default-item-art.d.mts +28 -0
- package/types/sohl/index.d.mts +7 -0
- package/types/sohl/item-builders.d.mts +11 -0
- package/types/sohl/item-fields.d.mts +20 -0
- package/types/sohl/items.d.mts +19 -0
- package/types/sohl/kb-passes.d.mts +71 -0
- package/types/sohl/note-schemas.d.mts +7 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Running the shared prose conventions over a repository — Prettier for
|
|
16
|
+
* formatting, markdownlint for the structure Prettier is indifferent to (#69).
|
|
17
|
+
*
|
|
18
|
+
* Both are thin: the rules live in `./prose-config.mjs` and the tools are
|
|
19
|
+
* Prettier and markdownlint themselves. What this module adds is the two things
|
|
20
|
+
* a consumer would otherwise have to get right on its own — supplying the
|
|
21
|
+
* shared configuration *as a default a local one overrides*, and reporting
|
|
22
|
+
* findings in the one parseable form every check in this package emits (#17).
|
|
23
|
+
*
|
|
24
|
+
* **Neither tool's own file discovery is reimplemented.** Prettier decides what
|
|
25
|
+
* it formats and what an ignore file excludes, through `getFileInfo`;
|
|
26
|
+
* markdownlint expands its own globs and honours `.gitignore` itself. A second
|
|
27
|
+
* implementation of either would drift from the tool it stands in for, and the
|
|
28
|
+
* whole point of the command is that `content-build format --check` and a bare
|
|
29
|
+
* `prettier --check .` report the same thing.
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import fs from "node:fs";
|
|
35
|
+
import path from "node:path";
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
MARKDOWNLINT_CONFIG,
|
|
39
|
+
MARKDOWN_GLOBS,
|
|
40
|
+
MARKDOWN_IGNORES,
|
|
41
|
+
sharedPrettierOptionsFor,
|
|
42
|
+
} from "./prose-config.mjs";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Directories never walked, whatever the ignore files say.
|
|
46
|
+
*
|
|
47
|
+
* `node_modules` is in every consumer's `.gitignore` and would be excluded
|
|
48
|
+
* anyway; skipping it in the walk rather than per file is the difference
|
|
49
|
+
* between a check that takes a second and one that stats a hundred thousand
|
|
50
|
+
* files to be told each is ignored. `.git` is in nobody's `.gitignore`, so it
|
|
51
|
+
* has to be named.
|
|
52
|
+
*/
|
|
53
|
+
const NEVER_WALK = Object.freeze(new Set([".git", "node_modules"]));
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The ignore files Prettier consults, in its own default order.
|
|
57
|
+
*
|
|
58
|
+
* Stated explicitly because passing `ignorePath` at all replaces the default,
|
|
59
|
+
* and the default is both of these — dropping `.gitignore` would start
|
|
60
|
+
* reporting on build output.
|
|
61
|
+
*/
|
|
62
|
+
const IGNORE_FILES = Object.freeze([".gitignore", ".prettierignore"]);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Every file under a root, minus the directories nothing should walk.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} root - Absolute path to walk.
|
|
68
|
+
* @returns {string[]} Absolute file paths, in directory order.
|
|
69
|
+
*/
|
|
70
|
+
function walkFiles(root) {
|
|
71
|
+
const out = [];
|
|
72
|
+
/** @param {string} dir - Directory to descend into. */
|
|
73
|
+
const descend = (dir) => {
|
|
74
|
+
let entries;
|
|
75
|
+
try {
|
|
76
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
77
|
+
} catch {
|
|
78
|
+
// A directory that vanished or cannot be read is not a finding
|
|
79
|
+
// about prose; leave it to whatever owns it.
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
const abs = path.join(dir, entry.name);
|
|
84
|
+
if (entry.isDirectory()) {
|
|
85
|
+
if (!NEVER_WALK.has(entry.name)) descend(abs);
|
|
86
|
+
} else if (entry.isFile()) {
|
|
87
|
+
out.push(abs);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
descend(root);
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check — or rewrite — every file Prettier claims, under one root.
|
|
97
|
+
*
|
|
98
|
+
* The configuration resolution is the part that matters: a consumer's own
|
|
99
|
+
* Prettier config, found by Prettier walking up from each file, always wins.
|
|
100
|
+
* {@link sharedPrettierOptionsFor} is what a file gets when that search finds
|
|
101
|
+
* nothing, which is the case in a repository that has deliberately declared
|
|
102
|
+
* none.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} root - Repository (or subtree) to check.
|
|
105
|
+
* @param {object} [opts]
|
|
106
|
+
* @param {readonly string[]} [opts.paths] - Files or directories to check
|
|
107
|
+
* instead of the whole root.
|
|
108
|
+
* @param {boolean} [opts.write=false] - Rewrite unformatted files in place
|
|
109
|
+
* rather than reporting them.
|
|
110
|
+
* @param {object} [opts.prettier] - The Prettier module, for tests.
|
|
111
|
+
* @returns {Promise<{findings: Array<{file: string, severity: string,
|
|
112
|
+
* message: string}>, checked: number, written: string[]}>} The findings, how
|
|
113
|
+
* many files were considered, and what was rewritten.
|
|
114
|
+
*/
|
|
115
|
+
export async function checkFormatting(root, opts = {}) {
|
|
116
|
+
const { paths, write = false } = opts;
|
|
117
|
+
const prettier = opts.prettier ?? (await import("prettier"));
|
|
118
|
+
const base = path.resolve(root);
|
|
119
|
+
const ignorePath = IGNORE_FILES.map((name) => path.join(base, name)).filter(
|
|
120
|
+
(file) => fs.existsSync(file),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const roots =
|
|
124
|
+
paths?.length ?
|
|
125
|
+
paths.map((entry) => path.resolve(base, entry))
|
|
126
|
+
: [base];
|
|
127
|
+
const candidates = roots.flatMap((entry) =>
|
|
128
|
+
fs.existsSync(entry) && fs.statSync(entry).isDirectory() ?
|
|
129
|
+
walkFiles(entry)
|
|
130
|
+
: [entry],
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const findings = [];
|
|
134
|
+
const written = [];
|
|
135
|
+
let checked = 0;
|
|
136
|
+
|
|
137
|
+
for (const file of candidates) {
|
|
138
|
+
const info = await prettier.getFileInfo(file, { ignorePath });
|
|
139
|
+
// `ignored` is the ignore files' answer; a null parser means Prettier
|
|
140
|
+
// has no opinion about this kind of file at all (an image, a lockfile
|
|
141
|
+
// it was told to skip). Neither is a finding.
|
|
142
|
+
if (info.ignored || !info.inferredParser) continue;
|
|
143
|
+
checked += 1;
|
|
144
|
+
|
|
145
|
+
const local = await prettier.resolveConfig(file, {
|
|
146
|
+
editorconfig: false,
|
|
147
|
+
});
|
|
148
|
+
// `resolveConfig` has already applied any `overrides` the consumer's
|
|
149
|
+
// own config declares. The shared fallback has to apply its own, since
|
|
150
|
+
// Prettier ignores an `overrides` block passed inline (#76).
|
|
151
|
+
const options = {
|
|
152
|
+
...(local ?? sharedPrettierOptionsFor(file)),
|
|
153
|
+
filepath: file,
|
|
154
|
+
};
|
|
155
|
+
const source = fs.readFileSync(file, "utf8");
|
|
156
|
+
|
|
157
|
+
// A file Prettier cannot parse is a finding, not a crash. Its own CLI
|
|
158
|
+
// reports the syntax error and carries on to the next file, and a
|
|
159
|
+
// single unparseable file in a large tree must not cost the report on
|
|
160
|
+
// every other one.
|
|
161
|
+
try {
|
|
162
|
+
if (write) {
|
|
163
|
+
const formatted = await prettier.format(source, options);
|
|
164
|
+
if (formatted !== source) {
|
|
165
|
+
fs.writeFileSync(file, formatted);
|
|
166
|
+
written.push(file);
|
|
167
|
+
}
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!(await prettier.check(source, options))) {
|
|
172
|
+
findings.push({
|
|
173
|
+
file,
|
|
174
|
+
severity: "error",
|
|
175
|
+
// No line or column: Prettier's answer is about the whole
|
|
176
|
+
// file, and #17's rule is to drop a field rather than
|
|
177
|
+
// invent one.
|
|
178
|
+
message:
|
|
179
|
+
"is not formatted; run `content-build format --write` to fix it",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
} catch (err) {
|
|
183
|
+
// Prettier hangs a `loc` off a syntax error, shaped `{start:
|
|
184
|
+
// {line, column}}` — a range, not a point. Reading `loc.line`
|
|
185
|
+
// directly finds `undefined` and yields a locator-less finding for
|
|
186
|
+
// the one class of finding that has an exact position. The wrapped
|
|
187
|
+
// parser error underneath carries a flat, 0-based `loc`; the outer
|
|
188
|
+
// one is 1-based and agrees with the message, so it wins.
|
|
189
|
+
const loc = err?.loc?.start ?? err?.loc;
|
|
190
|
+
findings.push({
|
|
191
|
+
file,
|
|
192
|
+
...(Number.isFinite(loc?.line) ?
|
|
193
|
+
{ line: loc.line, column: loc.column }
|
|
194
|
+
: {}),
|
|
195
|
+
severity: "error",
|
|
196
|
+
message: `cannot be parsed: ${String(err?.message ?? err).split("\n")[0]}`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return { findings, checked, written };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* One markdownlint result as a diagnostic.
|
|
206
|
+
*
|
|
207
|
+
* @param {string} directory - The root the result's `fileName` is relative to.
|
|
208
|
+
* @param {object} result - A markdownlint `LintResult`.
|
|
209
|
+
* @returns {{file: string, line: number, column?: number, severity: string,
|
|
210
|
+
* message: string}} The diagnostic.
|
|
211
|
+
*/
|
|
212
|
+
function toDiagnostic(directory, result) {
|
|
213
|
+
const {
|
|
214
|
+
fileName,
|
|
215
|
+
lineNumber,
|
|
216
|
+
ruleNames = [],
|
|
217
|
+
ruleDescription,
|
|
218
|
+
errorDetail,
|
|
219
|
+
errorContext,
|
|
220
|
+
errorRange,
|
|
221
|
+
} = result;
|
|
222
|
+
// `ruleNames` is [id, name, ...aliases]; the pair is what markdownlint's own
|
|
223
|
+
// output shows and what a reader searches the rule set for.
|
|
224
|
+
const rule = ruleNames.slice(0, 2).join("/");
|
|
225
|
+
const detail = errorDetail ? ` [${errorDetail}]` : "";
|
|
226
|
+
const context = errorContext ? ` [Context: "${errorContext}"]` : "";
|
|
227
|
+
return {
|
|
228
|
+
file: path.resolve(directory, fileName),
|
|
229
|
+
line: lineNumber,
|
|
230
|
+
// `errorRange` is `[column, length]`, 1-based, and absent for a finding
|
|
231
|
+
// about a whole line.
|
|
232
|
+
...(Array.isArray(errorRange) ? { column: errorRange[0] } : {}),
|
|
233
|
+
severity: "error",
|
|
234
|
+
message: `${rule} ${ruleDescription}${detail}${context}`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Lint a repository's markdown against the shared rule set.
|
|
240
|
+
*
|
|
241
|
+
* {@link MARKDOWNLINT_CONFIG} is passed as markdownlint's `optionsDefault`,
|
|
242
|
+
* which is precisely the "shipped default, consumer overrides" behaviour the
|
|
243
|
+
* command promises: a `.markdownlint-cli2.jsonc` found in the tree replaces it,
|
|
244
|
+
* and a repository with none gets these rules.
|
|
245
|
+
*
|
|
246
|
+
* @param {string} root - Repository to lint.
|
|
247
|
+
* @param {object} [opts]
|
|
248
|
+
* @param {readonly string[]} [opts.paths] - Globs to lint instead of every
|
|
249
|
+
* markdown file.
|
|
250
|
+
* @param {boolean} [opts.fix=false] - Apply the fixes markdownlint can make.
|
|
251
|
+
* @param {(params: object) => Promise<number>} [opts.run] - The
|
|
252
|
+
* markdownlint-cli2 entry point, for tests.
|
|
253
|
+
* @returns {Promise<{findings: object[], exitCode: number}>} The findings, and
|
|
254
|
+
* markdownlint's own exit code.
|
|
255
|
+
*/
|
|
256
|
+
export async function lintMarkdown(root, opts = {}) {
|
|
257
|
+
const { paths, fix = false } = opts;
|
|
258
|
+
const run = opts.run ?? (await import("markdownlint-cli2")).main;
|
|
259
|
+
const directory = path.resolve(root);
|
|
260
|
+
|
|
261
|
+
/** @type {object[]} */
|
|
262
|
+
const collected = [];
|
|
263
|
+
// A formatter is normally a module id resolved from disk, but
|
|
264
|
+
// markdownlint-cli2 passes a non-string through untouched — so the results
|
|
265
|
+
// can be collected in process rather than parsed back out of its output.
|
|
266
|
+
const collect = ({ results }) => {
|
|
267
|
+
collected.push(...results);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const argv = [...(paths?.length ? paths : MARKDOWN_GLOBS)];
|
|
271
|
+
if (fix) argv.push("--fix");
|
|
272
|
+
|
|
273
|
+
const exitCode = await run({
|
|
274
|
+
directory,
|
|
275
|
+
argv,
|
|
276
|
+
optionsDefault: {
|
|
277
|
+
config: MARKDOWNLINT_CONFIG,
|
|
278
|
+
// Generated markdown reports on its generator, not its author, and
|
|
279
|
+
// every consumer already lists its generated trees here.
|
|
280
|
+
gitignore: true,
|
|
281
|
+
ignores: [...MARKDOWN_IGNORES],
|
|
282
|
+
noProgress: true,
|
|
283
|
+
noBanner: true,
|
|
284
|
+
},
|
|
285
|
+
optionsOverride: { outputFormatters: [[collect]] },
|
|
286
|
+
logMessage: () => {},
|
|
287
|
+
logError: () => {},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
findings: collected.map((result) => toDiagnostic(directory, result)),
|
|
292
|
+
exitCode,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The curated Foundry region-event vocabulary, as plain data.
|
|
16
|
+
*
|
|
17
|
+
* Deliberately **plain ESM** — no TypeScript, no `@src` aliases, no Foundry —
|
|
18
|
+
* for the same reason `../sohl/default-item-art.mjs` is: the map-note pack
|
|
19
|
+
* compiler runs under bare `node`, outside the bundler that resolves `@src` and
|
|
20
|
+
* strips types, and it must reject an authored region event that the runtime
|
|
21
|
+
* would silently drop. One list here is what keeps the build-time lint and the
|
|
22
|
+
* runtime bridge from drifting apart.
|
|
23
|
+
*
|
|
24
|
+
* It sits in this package rather than in the system's `src/` tree because the
|
|
25
|
+
* map-note compiler that reads it is installed as a dependency (#1501), and a
|
|
26
|
+
* relative path out of the package would resolve to garbage from
|
|
27
|
+
* `node_modules`. The runtime reaches it back through the package's
|
|
28
|
+
* `./engine/region-events` entry point (#1510). It is engine-side, not
|
|
29
|
+
* SoHL-side, because any content module that authors a scene region — an
|
|
30
|
+
* adventure module included — needs this vocabulary.
|
|
31
|
+
*
|
|
32
|
+
* The prose explaining *why* each event is curated or excluded lives with the
|
|
33
|
+
* typed re-exports in `src/entity/event/region-triggers.ts`, which is the
|
|
34
|
+
* documented surface.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The curated Foundry region-event → SoHL trigger-name map. The keys are the
|
|
39
|
+
* `CONST.REGION_EVENTS` string values SoHL forwards.
|
|
40
|
+
*
|
|
41
|
+
* @type {Readonly<Record<string, string>>}
|
|
42
|
+
*/
|
|
43
|
+
export const REGION_EVENT_TO_TRIGGER = Object.freeze({
|
|
44
|
+
tokenEnter: "regionTokenEnter",
|
|
45
|
+
tokenExit: "regionTokenExit",
|
|
46
|
+
tokenTurnStart: "regionTokenTurnStart",
|
|
47
|
+
tokenTurnEnd: "regionTokenTurnEnd",
|
|
48
|
+
tokenRoundStart: "regionTokenRoundStart",
|
|
49
|
+
tokenRoundEnd: "regionTokenRoundEnd",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The Foundry region-event names SoHL forwards (the keys of the map).
|
|
54
|
+
*
|
|
55
|
+
* @type {readonly string[]}
|
|
56
|
+
*/
|
|
57
|
+
export const CURATED_REGION_EVENTS = Object.freeze(
|
|
58
|
+
Object.keys(REGION_EVENT_TO_TRIGGER),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Region events SoHL deliberately does **not** forward: the continuous
|
|
63
|
+
* (`tokenMove*`), view-dependent (`tokenAnimate*`) and lifecycle
|
|
64
|
+
* (`behavior*`, `regionBoundary`) streams.
|
|
65
|
+
*
|
|
66
|
+
* @type {readonly string[]}
|
|
67
|
+
*/
|
|
68
|
+
export const EXCLUDED_REGION_EVENTS = Object.freeze([
|
|
69
|
+
"tokenMoveIn",
|
|
70
|
+
"tokenMoveOut",
|
|
71
|
+
"tokenMoveWithin",
|
|
72
|
+
"tokenAnimateIn",
|
|
73
|
+
"tokenAnimateOut",
|
|
74
|
+
"regionBoundary",
|
|
75
|
+
"behaviorActivated",
|
|
76
|
+
"behaviorDeactivated",
|
|
77
|
+
"behaviorViewed",
|
|
78
|
+
"behaviorUnviewed",
|
|
79
|
+
]);
|