@heroiclands/package-build 22.15.0 → 22.16.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.
- package/CHANGELOG.md +103 -0
- package/README.md +39 -678
- package/bin/{content-build.mjs → content-commands.mjs} +197 -118
- package/bin/package-build.mjs +303 -42
- package/bin/report.mjs +1 -1
- package/ci/ci-docker.mjs +6 -3
- package/ci/ci-steps.mjs +6 -4
- package/ci/content-format-reference.mjs +106 -0
- package/config.mjs +22 -2
- package/content-config.mjs +74 -134
- package/docs/api.md +294 -234
- package/docs/authoring/assets.md +31 -0
- package/docs/authoring/dates-and-calendars.md +33 -0
- package/docs/authoring/first-note.md +48 -0
- package/docs/authoring/frontmatter.md +40 -0
- package/docs/authoring/links-and-markup.md +137 -0
- package/docs/commands.md +631 -162
- package/docs/configuration.md +176 -140
- package/docs/content-format.md +50 -4229
- package/docs/design.md +32 -0
- package/docs/diagnostics.md +29 -22
- package/docs/getting-started.md +21 -877
- package/docs/guides/book.md +153 -0
- package/docs/guides/e2e.md +143 -0
- package/docs/guides/icons.md +126 -0
- package/docs/guides/localization.md +120 -0
- package/docs/guides/module-project.md +915 -0
- package/docs/guides/packs.md +167 -0
- package/docs/guides/reference-pages.md +148 -0
- package/docs/guides/release-and-deploy.md +114 -0
- package/docs/guides/site.md +152 -0
- package/docs/guides/system-project.md +45 -0
- package/docs/index.md +16 -0
- package/docs/project-setup.md +54 -35
- package/docs/reference/format-details.md +4420 -0
- package/docs/reference/note-types.md +531 -0
- package/engine/address-render.mjs +26 -0
- package/engine/address.mjs +3 -20
- package/engine/anchors.mjs +15 -6
- package/engine/asset-bindings.mjs +6 -2
- package/engine/base-compiler.mjs +47 -11
- package/engine/being-measurements.mjs +70 -0
- package/engine/calendar-artifacts.mjs +1 -1
- package/engine/calendar-choice.mjs +32 -0
- package/engine/calendar-human.mjs +71 -0
- package/engine/calendar-notes.mjs +41 -3
- package/engine/calendars.mjs +8 -1
- package/engine/changelog-lint.mjs +2 -2
- package/engine/code-fences.mjs +23 -97
- package/engine/compile-corpus.mjs +6 -7
- package/engine/content-admonitions.mjs +114 -0
- package/engine/content-captions.mjs +181 -0
- package/engine/content-embeds.mjs +2 -2
- package/engine/content-footnotes.mjs +34 -0
- package/engine/content-format-check.mjs +2 -2
- package/engine/content-format.mjs +127 -73
- package/engine/content-format.yaml +745 -0
- package/engine/content-html.mjs +1 -1
- package/engine/content-icons.mjs +45 -92
- package/engine/content-images.mjs +22 -74
- package/engine/content-index.mjs +12 -3
- package/engine/content-links.mjs +21 -26
- package/engine/content-lint.mjs +1 -5
- package/engine/content-secrets.mjs +3 -1
- package/engine/content-tables.mjs +47 -1257
- package/engine/culture-choice.mjs +32 -0
- package/engine/date-conversion.mjs +47 -0
- package/engine/declaration-check.mjs +87 -0
- package/engine/docs-checks.mjs +140 -0
- package/engine/documentation-links.mjs +31 -0
- package/engine/exported-scene.mjs +1 -1
- package/engine/extension-attributes.mjs +74 -0
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +4 -0
- package/engine/foreign-catalog.mjs +5 -5
- package/engine/format-generated.mjs +15 -0
- package/engine/foundry-entries.mjs +3 -1
- package/engine/frontmatter-lint.mjs +5 -1
- package/engine/frontmatter.mjs +10 -0
- package/engine/generate.mjs +2 -2
- package/engine/helpers.mjs +71 -117
- package/engine/homepage.mjs +2 -3
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +18 -3
- package/engine/infobox-registry.mjs +3 -1
- package/engine/infobox.mjs +38 -20
- package/engine/item-compiler.mjs +10 -8
- package/engine/item-docs.mjs +7 -6
- package/engine/itinerary-scenes.mjs +4 -2
- package/engine/journals.mjs +51 -10
- package/engine/macros.mjs +1 -1
- package/engine/map-build.mjs +1 -1
- package/engine/map-dot.mjs +1 -1
- package/engine/map-raster.mjs +4 -2
- package/engine/markdown-expressions.mjs +117 -0
- package/engine/metadata-index.mjs +7 -3
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-dates.mjs +283 -61
- package/engine/note-package.mjs +5 -25
- package/engine/note-state.mjs +6 -2
- package/engine/note-vocabulary.mjs +74 -19
- package/engine/number-words.mjs +75 -0
- package/engine/office-holders.mjs +188 -0
- package/engine/pack-config.mjs +2 -15
- package/engine/pack-router.mjs +24 -5
- package/engine/packages.mjs +3 -2
- package/engine/pathnames.mjs +21 -24
- package/engine/pdf-build.mjs +217 -55
- package/engine/pdf-fonts.mjs +2 -2
- package/engine/pdf-images.mjs +82 -0
- package/engine/pdf-render.mjs +174 -22
- package/engine/populations.mjs +3 -2
- package/engine/project-init.mjs +499 -0
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +7 -6
- package/engine/readability-lint.mjs +86 -0
- package/engine/reckoning-markers.mjs +205 -93
- package/engine/scenes.mjs +5 -2
- package/engine/schema-check.mjs +3 -3
- package/engine/site-build.mjs +87 -37
- package/engine/site-config.mjs +8 -8
- package/engine/site-index.mjs +4 -0
- package/engine/site-maps.mjs +1 -1
- package/engine/site-preview.mjs +19 -8
- package/engine/social-ties.mjs +5 -1
- package/engine/sql-tables.mjs +92 -22
- package/engine/web-wikilinks.mjs +15 -31
- package/engine/wikilink-syntax.mjs +4 -1
- package/engine/wikilinks.mjs +12 -14
- package/hm3/item-fields.mjs +1 -1
- package/hm3/items.mjs +12 -45
- package/index.mjs +2 -14
- package/manifest.mjs +31 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +32 -11
- package/prettier-config.mjs +1 -1
- package/sohl/being-info.mjs +3 -145
- package/sohl/index.mjs +1 -1
- package/sohl/infobox.mjs +2 -1
- package/sohl/item-builders.mjs +3 -27
- package/sohl/item-fields.mjs +61 -22
- package/sohl/kb-passes.mjs +2 -2
- package/sohl/note-schemas.mjs +0 -8
- package/stage.mjs +19 -0
- package/types/config.d.mts +12 -3
- package/types/content-config.d.mts +15 -81
- package/types/engine/address-render.d.mts +13 -0
- package/types/engine/address.d.mts +2 -11
- package/types/engine/anchors.d.mts +3 -4
- package/types/engine/asset-bindings.d.mts +9 -2
- package/types/engine/base-compiler.d.mts +1 -2
- package/types/engine/being-measurements.d.mts +16 -0
- package/types/engine/calendar-choice.d.mts +14 -0
- package/types/engine/calendar-human.d.mts +4 -0
- package/types/engine/calendar-notes.d.mts +2 -0
- package/types/engine/calendars.d.mts +5 -3
- package/types/engine/code-fences.d.mts +6 -72
- package/types/engine/compile-corpus.d.mts +1 -3
- package/types/engine/content-admonitions.d.mts +24 -0
- package/types/engine/content-captions.d.mts +45 -0
- package/types/engine/content-footnotes.d.mts +10 -0
- package/types/engine/content-format-check.d.mts +17 -4
- package/types/engine/content-format.d.mts +24 -11
- package/types/engine/content-icons.d.mts +14 -23
- package/types/engine/content-images.d.mts +2 -2
- package/types/engine/content-index.d.mts +9 -2
- package/types/engine/content-links.d.mts +3 -5
- package/types/engine/content-tables.d.mts +11 -140
- package/types/engine/culture-choice.d.mts +14 -0
- package/types/engine/date-conversion.d.mts +4 -0
- package/types/engine/declaration-check.d.mts +24 -0
- package/types/engine/docs-checks.d.mts +28 -0
- package/types/engine/documentation-links.d.mts +7 -0
- package/types/engine/exported-scene.d.mts +6 -2
- package/types/engine/extension-attributes.d.mts +14 -0
- package/types/engine/field-spec.d.mts +9 -0
- package/types/engine/format-generated.d.mts +7 -0
- package/types/engine/frontmatter.d.mts +14 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +37 -92
- package/types/engine/homepage.d.mts +2 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +3 -1
- package/types/engine/infobox.d.mts +17 -6
- package/types/engine/item-compiler.d.mts +4 -4
- package/types/engine/item-docs.d.mts +4 -3
- package/types/engine/journals.d.mts +10 -7
- package/types/engine/map-graphviz.d.mts +1 -1
- package/types/engine/markdown-expressions.d.mts +17 -0
- package/types/engine/note-dates.d.mts +47 -1
- package/types/engine/note-package.d.mts +3 -19
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/note-vocabulary.d.mts +9 -0
- package/types/engine/number-words.d.mts +4 -0
- package/types/engine/office-holders.d.mts +14 -0
- package/types/engine/pack-config.d.mts +1 -1
- package/types/engine/packages.d.mts +3 -2
- package/types/engine/pdf-images.d.mts +38 -0
- package/types/engine/populations.d.mts +2 -2
- package/types/engine/project-init.d.mts +17 -0
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +3 -2
- package/types/engine/readability-lint.d.mts +22 -0
- package/types/engine/reckoning-markers.d.mts +4 -2
- package/types/engine/schema-check.d.mts +3 -3
- package/types/engine/site-build.d.mts +38 -28
- package/types/engine/site-config.d.mts +2 -2
- package/types/engine/site-index.d.mts +4 -0
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/social-ties.d.mts +8 -2
- package/types/engine/sql-tables.d.mts +13 -2
- package/types/engine/web-wikilinks.d.mts +2 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/hm3/items.d.mts +5 -6
- package/types/labels.d.mts +40 -0
- package/types/manifest.d.mts +3 -3
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/being-info.d.mts +3 -43
- package/types/sohl/index.d.mts +1 -1
- package/types/sohl/infobox.d.mts +5 -2
- package/types/sohl/item-fields.d.mts +4 -13
- package/types/stage.d.mts +9 -0
- package/CONTENT.md +0 -3239
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Render a note body with Foundry's secret section markup. */
|
|
2
|
-
export function renderFoundryMarkdown(body: any): string;
|
|
2
|
+
export function renderFoundryMarkdown(body: any, captions: any, footnoteNumbers: any, docId: any): string;
|
|
3
3
|
/**
|
|
4
4
|
* Parses a markdown file with YAML frontmatter.
|
|
5
5
|
*
|
|
@@ -11,19 +11,19 @@ export function renderFoundryMarkdown(body: any): string;
|
|
|
11
11
|
* {@link positionInBody}. If the file has no frontmatter block, returns
|
|
12
12
|
* `{ frontmatter: null, body: "", description: "" }` with a warn log, and no
|
|
13
13
|
* position: there is no body to have one.
|
|
14
|
+
* @param {string} filePath - Markdown file to read.
|
|
15
|
+
* @param {{addressContext?: object}} [opts] - Address defaults for decoded fields.
|
|
16
|
+
* @returns {{frontmatter: object|null, body: string, description: string,
|
|
17
|
+
* bodyLine?: number, bodyColumn?: number}} Parsed note and body position.
|
|
14
18
|
*/
|
|
15
|
-
export function parseMarkdownFile(filePath:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
bodyLine?: undefined;
|
|
20
|
-
bodyColumn?: undefined;
|
|
21
|
-
} | {
|
|
22
|
-
frontmatter: any;
|
|
19
|
+
export function parseMarkdownFile(filePath: string, { addressContext }?: {
|
|
20
|
+
addressContext?: object;
|
|
21
|
+
}): {
|
|
22
|
+
frontmatter: object | null;
|
|
23
23
|
body: string;
|
|
24
24
|
description: string;
|
|
25
|
-
bodyLine
|
|
26
|
-
bodyColumn
|
|
25
|
+
bodyLine?: number;
|
|
26
|
+
bodyColumn?: number;
|
|
27
27
|
};
|
|
28
28
|
/**
|
|
29
29
|
* Refuse a corpus read whose scope its caller did not state.
|
|
@@ -80,6 +80,7 @@ export function assertSuppliedCorpus(records: readonly object[] | undefined, who
|
|
|
80
80
|
* @param {readonly string[]} opts.skipDirectories - Directory names to ignore.
|
|
81
81
|
* Required: the scope is the caller's to state, so two passes cannot
|
|
82
82
|
* disagree about which files are the corpus.
|
|
83
|
+
* @param {object} [opts.addressContext] - Address resolution context.
|
|
83
84
|
* @yields {{frontmatter: object|null, body: string, description: string,
|
|
84
85
|
* file: string, absPath: string, bodyLine?: number, bodyColumn?: number}}
|
|
85
86
|
* One entry per `.md` file found.
|
|
@@ -88,22 +89,15 @@ export function assertSuppliedCorpus(records: readonly object[] | undefined, who
|
|
|
88
89
|
*/
|
|
89
90
|
export function walkMarkdownTree(rootDir: string, { skipDirectories, addressContext }?: {
|
|
90
91
|
skipDirectories: readonly string[];
|
|
92
|
+
addressContext?: object | undefined;
|
|
91
93
|
}): Generator<{
|
|
92
94
|
file: string & NonSharedBuffer;
|
|
93
95
|
absPath: string;
|
|
94
|
-
frontmatter: null;
|
|
96
|
+
frontmatter: object | null;
|
|
95
97
|
body: string;
|
|
96
98
|
description: string;
|
|
97
|
-
bodyLine?:
|
|
98
|
-
bodyColumn?:
|
|
99
|
-
} | {
|
|
100
|
-
file: string & NonSharedBuffer;
|
|
101
|
-
absPath: string;
|
|
102
|
-
frontmatter: any;
|
|
103
|
-
body: string;
|
|
104
|
-
description: string;
|
|
105
|
-
bodyLine: number;
|
|
106
|
-
bodyColumn: number;
|
|
99
|
+
bodyLine?: number;
|
|
100
|
+
bodyColumn?: number;
|
|
107
101
|
}, void, unknown>;
|
|
108
102
|
/**
|
|
109
103
|
* The template priority a note states, for a system that treats an unstated one
|
|
@@ -176,8 +170,11 @@ export function systemTemplatePriority(fm: object, label: string): number | null
|
|
|
176
170
|
/**
|
|
177
171
|
* Generates a compendium-source filename: `Name_id.json` with non-
|
|
178
172
|
* alphanumeric runs replaced by underscores.
|
|
173
|
+
* @param {string} name - Document name.
|
|
174
|
+
* @param {string} id - Document id.
|
|
175
|
+
* @returns {string} Filename for the document.
|
|
179
176
|
*/
|
|
180
|
-
export function makeFilename(name:
|
|
177
|
+
export function makeFilename(name: string, id: string): string;
|
|
181
178
|
/**
|
|
182
179
|
* Translate an authored pathname into the address a Foundry install serves.
|
|
183
180
|
*
|
|
@@ -228,8 +225,11 @@ export function resolveImg(raw: string | null | undefined, config?: {
|
|
|
228
225
|
/**
|
|
229
226
|
* Resolves the display name from frontmatter, preferring `name.full`,
|
|
230
227
|
* falling back to `name` (if string), then `defaultValue`.
|
|
228
|
+
* @param {object|null|undefined} fm - Parsed frontmatter.
|
|
229
|
+
* @param {string} [defaultValue] - Name when the note provides none.
|
|
230
|
+
* @returns {string} Display name.
|
|
231
231
|
*/
|
|
232
|
-
export function resolveName(fm:
|
|
232
|
+
export function resolveName(fm: object | null | undefined, defaultValue?: string): string;
|
|
233
233
|
/**
|
|
234
234
|
* The oldest Foundry core this package supports, stamped into every compiled
|
|
235
235
|
* document as `_stats.coreVersion`.
|
|
@@ -345,21 +345,15 @@ export function defaultStats(): object;
|
|
|
345
345
|
* the index and the compile agree about where each note landed; defaults to
|
|
346
346
|
* this repository's own.
|
|
347
347
|
* @param {object} [opts]
|
|
348
|
-
* @param {readonly string[]} [opts.skipDirectories] - Part of the options bag
|
|
349
|
-
* every corpus reader takes; the scope is already settled by `records`.
|
|
350
348
|
* @param {object} [opts.config] - The resolved build configuration; loaded when
|
|
351
349
|
* omitted.
|
|
352
350
|
* @param {readonly object[]} [opts.records] - The corpus, derived once per
|
|
353
351
|
* compile and handed in. Required: see {@link assertSuppliedCorpus}.
|
|
354
|
-
* @param {object[]} [opts.problems] - Part of the same options bag; the notes
|
|
355
|
-
* the index cannot record are collected where the corpus is derived.
|
|
356
352
|
* @returns {{byShortcode: Map, types: Set}} From `buildWikilinkIndex`.
|
|
357
353
|
*/
|
|
358
|
-
export function buildContentLinkIndex(contentBase: string, router?: object, {
|
|
359
|
-
skipDirectories?: readonly string[] | undefined;
|
|
354
|
+
export function buildContentLinkIndex(contentBase: string, router?: object, { config, records }?: {
|
|
360
355
|
config?: object | undefined;
|
|
361
356
|
records?: readonly object[] | undefined;
|
|
362
|
-
problems?: object[] | undefined;
|
|
363
357
|
}): {
|
|
364
358
|
byShortcode: Map<any, any>;
|
|
365
359
|
types: Set<any>;
|
|
@@ -421,68 +415,16 @@ export function convertNoteWikilinks(body: string, { type, id, pack, docPack, in
|
|
|
421
415
|
unresolved: Array<object>;
|
|
422
416
|
};
|
|
423
417
|
/**
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
* @param {
|
|
429
|
-
* @param {object} [
|
|
430
|
-
* @
|
|
431
|
-
* every corpus reader takes; the scope is already settled by `records`.
|
|
432
|
-
* @param {object} [opts.config] - The resolved build configuration; loaded when
|
|
433
|
-
* omitted.
|
|
434
|
-
* @param {readonly object[]} [opts.records] - The corpus, derived once per
|
|
435
|
-
* compile and handed in. Required: see {@link assertSuppliedCorpus}.
|
|
436
|
-
* @param {object[]} [opts.problems] - Part of the same options bag; the notes
|
|
437
|
-
* the walk cannot read are collected where the corpus is derived.
|
|
438
|
-
* @returns {Array<{fm: object, path: string, tld: string, folder: string,
|
|
439
|
-
* absPath: string}>}
|
|
440
|
-
*/
|
|
441
|
-
export function collectContentDocs(contentBase: string, { skipDirectories, config, records, problems }?: {
|
|
442
|
-
skipDirectories?: readonly string[] | undefined;
|
|
443
|
-
config?: object | undefined;
|
|
444
|
-
records?: readonly object[] | undefined;
|
|
445
|
-
problems?: object[] | undefined;
|
|
446
|
-
}): Array<{
|
|
447
|
-
fm: object;
|
|
448
|
-
path: string;
|
|
449
|
-
tld: string;
|
|
450
|
-
folder: string;
|
|
451
|
-
absPath: string;
|
|
452
|
-
}>;
|
|
453
|
-
/**
|
|
454
|
-
* Expand the fenced `dataview` tables in one note's markdown, before wikilinks
|
|
455
|
-
* are resolved — so a generated cell may itself be a wikilink.
|
|
456
|
-
*
|
|
457
|
-
* A table searches the whole tree, which is one package's notes and nothing
|
|
458
|
-
* else — so there is no longer a package to scope on. It used to filter, back
|
|
459
|
-
* when a tree could hold several packages' notes and `package:` said which was
|
|
460
|
-
* which; that field is retired and the filter with it.
|
|
461
|
-
*
|
|
462
|
-
* @param {string} body - The note's markdown body.
|
|
463
|
-
* @param {object} ctx
|
|
464
|
-
* @param {Array<object>} ctx.docs - From {@link collectContentDocs}.
|
|
465
|
-
* @param {string} ctx.name - The note, for the error message.
|
|
466
|
-
* @param {object} [ctx.fm] - The source note's frontmatter, which is what a
|
|
467
|
-
* query's `this` reads. Its entry in `docs` supplies the path as well.
|
|
468
|
-
* @param {number} [ctx.bodyLine] - 1-based file line of the body's first line,
|
|
469
|
-
* so a failing directive can be reported at its position in the file.
|
|
470
|
-
* @param {object[]} [ctx.sqlTables] - This note's prepared `sql` results, in
|
|
471
|
-
* document order, from
|
|
472
|
-
* {@link module:engine/sql-tables.prepareSqlTables}. An `sql` directive with
|
|
473
|
-
* no prepared result fails the note: nothing here runs a query.
|
|
474
|
-
* @returns {{markdown: string, lineMap: Array<{line: number,
|
|
475
|
-
* generated: boolean}>}} The body with every table expanded, and where each
|
|
476
|
-
* emitted line came from — which is what lets a diagnostic about the
|
|
477
|
-
* expanded body name an authored position.
|
|
478
|
-
* @throws {Error} When a query is malformed or unsupported — the note fails to
|
|
479
|
-
* compile rather than shipping a table-shaped hole. The error carries
|
|
480
|
-
* `position`, the directive's own line.
|
|
418
|
+
* Expand prepared SQL tables before resolving wikilinks in generated cells.
|
|
419
|
+
* @param {string} body - The note body.
|
|
420
|
+
* @param {object} ctx - Source and prepared query results.
|
|
421
|
+
* @param {string} ctx.name - Name used in diagnostics.
|
|
422
|
+
* @param {number} [ctx.bodyLine] - The first body line in the source file.
|
|
423
|
+
* @param {object[]} [ctx.sqlTables] - Prepared SQL results in document order.
|
|
424
|
+
* @returns {{markdown: string, lineMap: Array<{line: number, generated: boolean}>}}
|
|
481
425
|
*/
|
|
482
|
-
export function expandNoteTables(body: string, {
|
|
483
|
-
docs: Array<object>;
|
|
426
|
+
export function expandNoteTables(body: string, { name, bodyLine, sqlTables }: {
|
|
484
427
|
name: string;
|
|
485
|
-
fm?: object | undefined;
|
|
486
428
|
bodyLine?: number | undefined;
|
|
487
429
|
sqlTables?: object[] | undefined;
|
|
488
430
|
}): {
|
|
@@ -496,8 +438,11 @@ export function expandNoteTables(body: string, { docs, name, fm, bodyLine, sqlTa
|
|
|
496
438
|
* Builds a compendium-source filename for a folder JSON document:
|
|
497
439
|
* `folder_Name_id.json` with non-alphanumeric runs replaced by
|
|
498
440
|
* underscores.
|
|
441
|
+
* @param {string} name - Folder name.
|
|
442
|
+
* @param {string} id - Folder id.
|
|
443
|
+
* @returns {string} Filename for the folder.
|
|
499
444
|
*/
|
|
500
|
-
export function folderFilename(name:
|
|
445
|
+
export function folderFilename(name: string, id: string): string;
|
|
501
446
|
/**
|
|
502
447
|
* The markdown renderer every surface shares.
|
|
503
448
|
*
|
|
@@ -87,9 +87,8 @@ export function checkHomepageAddressFields(fm: object | null | undefined, { isAu
|
|
|
87
87
|
* {@link module:engine/diagnostics}. {@link lintContentTree} already reports an
|
|
88
88
|
* empty walk against the same locator.
|
|
89
89
|
*
|
|
90
|
-
* The rule
|
|
91
|
-
*
|
|
92
|
-
* published, and the homepage is the floor underneath both modes.
|
|
90
|
+
* The rule applies to every content tree, whether it contains only the
|
|
91
|
+
* homepage or other notes as well.
|
|
93
92
|
*
|
|
94
93
|
* @param {ReadonlyArray<{file: string}>} found - The homepage notes, in walk
|
|
95
94
|
* order. Paths may be absolute or relative to the working directory.
|
package/types/engine/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * as ids from "./ids.mjs";
|
|
2
2
|
export * as systemBlock from "./system-block.mjs";
|
|
3
3
|
export * as codeFences from "./code-fences.mjs";
|
|
4
|
+
export { formatGenerated } from "./format-generated.mjs";
|
|
4
5
|
export * as frontmatter from "./frontmatter.mjs";
|
|
5
6
|
export * as contentTree from "./content-tree.mjs";
|
|
6
7
|
export * as packConfig from "./pack-config.mjs";
|
|
@@ -31,6 +32,10 @@ export * as siteMaps from "./site-maps.mjs";
|
|
|
31
32
|
export * as systems from "./systems.mjs";
|
|
32
33
|
export * as calendars from "./calendars.mjs";
|
|
33
34
|
export * as noteDates from "./note-dates.mjs";
|
|
35
|
+
export * as dateConversion from "./date-conversion.mjs";
|
|
36
|
+
export * as markdownExpressions from "./markdown-expressions.mjs";
|
|
37
|
+
export * as calendarChoice from "./calendar-choice.mjs";
|
|
38
|
+
export * as cultureChoice from "./culture-choice.mjs";
|
|
34
39
|
export * as reckoningMarkers from "./reckoning-markers.mjs";
|
|
35
40
|
export * as calendarNotes from "./calendar-notes.mjs";
|
|
36
41
|
export * as calendarArtifacts from "./calendar-artifacts.mjs";
|
|
@@ -50,13 +50,15 @@ export function compilesSystemDocument(fm: object, map: object, router: object):
|
|
|
50
50
|
* Resolves a reference to `{name, url?, uuid?, address?, subType?}`.
|
|
51
51
|
* @param {object} [options.router] - The pack router deciding which system
|
|
52
52
|
* compiles a document for this note. Defaults to the consuming repository's.
|
|
53
|
+
* @param {object} [options.dates] - Calendar conversion context.
|
|
53
54
|
* @returns {object[]} The boxes, in the order every medium renders them.
|
|
54
55
|
* @throws {Error} When the built set disagrees with what the note's type maps
|
|
55
56
|
* to — see {@link module:engine/infobox.assertInfoboxSet}.
|
|
56
57
|
*/
|
|
57
|
-
export function noteInfoboxes(fm: object, { resolve, router }?: {
|
|
58
|
+
export function noteInfoboxes(fm: object, { resolve, router, dates }?: {
|
|
58
59
|
resolve?: ((ref: unknown, hint?: object) => object | undefined) | undefined;
|
|
59
60
|
router?: object | undefined;
|
|
61
|
+
dates?: object | undefined;
|
|
60
62
|
}): object[];
|
|
61
63
|
/**
|
|
62
64
|
* Every system's infobox declaration, in the order a page shows them.
|
|
@@ -236,10 +236,16 @@ export function isDeclaredDefault(field: object, raw: unknown): boolean;
|
|
|
236
236
|
*
|
|
237
237
|
* @param {object} fm - The note's frontmatter.
|
|
238
238
|
* @param {readonly object[]} fields - The system's field declaration.
|
|
239
|
-
* @param {
|
|
239
|
+
* @param {{block: string, resolve?: Function, resolveField?: Function, taken?: Set<string>, presentation?: object}} ctx - Rendering context.
|
|
240
240
|
* @returns {object[]} Zero or one section.
|
|
241
241
|
*/
|
|
242
|
-
export function systemRowsSection(fm: object, fields: readonly object[], { block, resolve, resolveField, taken, presentation }:
|
|
242
|
+
export function systemRowsSection(fm: object, fields: readonly object[], { block, resolve, resolveField, taken, presentation }: {
|
|
243
|
+
block: string;
|
|
244
|
+
resolve?: Function;
|
|
245
|
+
resolveField?: Function;
|
|
246
|
+
taken?: Set<string>;
|
|
247
|
+
presentation?: object;
|
|
248
|
+
}): object[];
|
|
243
249
|
/**
|
|
244
250
|
* Every box a note carries, in the order every medium renders them.
|
|
245
251
|
*
|
|
@@ -290,10 +296,12 @@ export function sectionHolds(section: object): boolean;
|
|
|
290
296
|
* against a restatement of it.
|
|
291
297
|
*
|
|
292
298
|
* @param {object} fm - The note's frontmatter.
|
|
293
|
-
* @param {object} options -
|
|
299
|
+
* @param {{maps: readonly object[]}} options - Document subtype mappings.
|
|
294
300
|
* @returns {string[]} The ids.
|
|
295
301
|
*/
|
|
296
|
-
export function requiredInfoboxIds(fm: object, { maps }:
|
|
302
|
+
export function requiredInfoboxIds(fm: object, { maps }: {
|
|
303
|
+
maps: readonly object[];
|
|
304
|
+
}): string[];
|
|
297
305
|
/**
|
|
298
306
|
* Refuse a page carrying anything but the boxes its type maps to.
|
|
299
307
|
*
|
|
@@ -306,12 +314,15 @@ export function requiredInfoboxIds(fm: object, { maps }: object): string[];
|
|
|
306
314
|
*
|
|
307
315
|
* @param {readonly object[]} boxes - What was built.
|
|
308
316
|
* @param {object} fm - The note's frontmatter.
|
|
309
|
-
* @param {object} options -
|
|
317
|
+
* @param {{maps: readonly object[], where?: string}} options - Document subtype mappings and context.
|
|
310
318
|
* @returns {readonly object[]} The boxes, unchanged, so a caller may assert
|
|
311
319
|
* inline.
|
|
312
320
|
* @throws {Error} Naming the note, what is missing and what is extra.
|
|
313
321
|
*/
|
|
314
|
-
export function assertInfoboxSet(boxes: readonly object[], fm: object, { maps, where }?:
|
|
322
|
+
export function assertInfoboxSet(boxes: readonly object[], fm: object, { maps, where }?: {
|
|
323
|
+
maps: readonly object[];
|
|
324
|
+
where?: string;
|
|
325
|
+
}): readonly object[];
|
|
315
326
|
/**
|
|
316
327
|
* How a section arranges what it holds.
|
|
317
328
|
*
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The description an item carries: a pointer to its **item doc**,
|
|
3
|
-
* JournalEntry the journals pass compiles this same body into.
|
|
2
|
+
* The description an item carries: a pointer to a page of its **item doc**,
|
|
3
|
+
* the JournalEntry the journals pass compiles this same body into.
|
|
4
4
|
*
|
|
5
5
|
* The prose is not rendered into the item at all. Carrying it would duplicate
|
|
6
6
|
* it onto every actor holding the item — 7.59 MB of copies across the actors
|
|
7
7
|
* pack, of which 133 KB was distinct — where a link is 60 bytes and always
|
|
8
8
|
* current. The two passes derive the target from the note's own id, so neither
|
|
9
|
-
* has to see the other's output
|
|
10
|
-
*
|
|
9
|
+
* has to see the other's output. `{#description}` selects the named page;
|
|
10
|
+
* an item without that anchor points to its first page.
|
|
11
11
|
*
|
|
12
12
|
* An item with no prose points at nothing, exactly as the journals pass writes
|
|
13
13
|
* no entry for it.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
export function itemDocEntryId(itemId: string): string;
|
|
19
19
|
/**
|
|
20
20
|
* The description an item carries in place of its prose: a `@UUID` link to the
|
|
21
|
-
*
|
|
21
|
+
* description page of its item doc, and nothing else.
|
|
22
22
|
*
|
|
23
23
|
* "Nothing else" is the whole convention — a description that is *only* a link
|
|
24
24
|
* is a pointer, and anything alongside it would make it ordinary prose that
|
|
@@ -30,11 +30,12 @@ export function itemDocEntryId(itemId: string): string;
|
|
|
30
30
|
* @param {string} name - The item's name, used as the link's label. It shows
|
|
31
31
|
* only if the target ever fails to resolve, where a broken link naming the
|
|
32
32
|
* item beats a bare UUID.
|
|
33
|
-
* @param {string}
|
|
33
|
+
* @param {string} pageId - The id of the selected entry page, from
|
|
34
34
|
* {@link journalPageId}.
|
|
35
|
+
* @param {object} [fm] - The source note, used to route GM-only prose.
|
|
35
36
|
* @returns {string} The pointer to store in `system.docHtml`.
|
|
36
37
|
*/
|
|
37
|
-
export function itemDocPointer(packageId: string, itemId: string, name: string,
|
|
38
|
+
export function itemDocPointer(packageId: string, itemId: string, name: string, pageId: string, fm?: object): string;
|
|
38
39
|
export { itemTypes };
|
|
39
40
|
import { itemTypes } from "./item-registry.mjs";
|
|
40
41
|
export { docEntryTypes, hasDocEntry } from "./address.mjs";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* a split. Content before the first H1 (if non-empty) becomes a leading
|
|
5
5
|
* page. Each H1 yields a page whose name is the heading
|
|
6
6
|
* text (with any `{#anchor-id}` suffix stripped out and surfaced as
|
|
7
|
-
* `
|
|
7
|
+
* `anchorSlug`).
|
|
8
8
|
*
|
|
9
9
|
* `leadName` names that leading page. A journal note's is "Introduction",
|
|
10
10
|
* because it introduces the pages that follow. An item doc's is the item — a
|
|
@@ -12,14 +12,17 @@
|
|
|
12
12
|
* calling that page "Introduction" would label the description as a preamble to
|
|
13
13
|
* nothing.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
15
|
+
* @param {string} body - Markdown body to split.
|
|
16
|
+
* @param {string} [leadName] - Name of the page before the first heading.
|
|
17
|
+
* @returns {Array<{name: string, anchorSlug: string|null, level: number,
|
|
18
|
+
* markdown: string}>} Pages in document order.
|
|
16
19
|
*/
|
|
17
|
-
export function splitPages(body:
|
|
20
|
+
export function splitPages(body: string, leadName?: string): Array<{
|
|
18
21
|
name: string;
|
|
19
|
-
anchorSlug: null;
|
|
22
|
+
anchorSlug: string | null;
|
|
20
23
|
level: number;
|
|
21
24
|
markdown: string;
|
|
22
|
-
}
|
|
25
|
+
}>;
|
|
23
26
|
/**
|
|
24
27
|
* Two pages in one note that would derive the same id, which the LevelDB packer
|
|
25
28
|
* reports only as an opaque duplicate-key collision. Catch it here, where the
|
|
@@ -66,7 +69,7 @@ export function assertUniqueAnchors(rawPages: Array<{
|
|
|
66
69
|
* An anchored page takes the id its inbound links compute from the note id and
|
|
67
70
|
* the slug, so link and page agree without shared state. Every other page is
|
|
68
71
|
* keyed by its **name**, which is what lets the items pass address an item
|
|
69
|
-
* doc's
|
|
72
|
+
* doc's selected page without having compiled it (see
|
|
70
73
|
* {@link sohl.utils.packs.itemDocPointer}).
|
|
71
74
|
*
|
|
72
75
|
* **It takes no index**. Keying a page by position *and* name,
|
|
@@ -100,7 +103,7 @@ export function journalPageId(entryId: string, page: {
|
|
|
100
103
|
* documents, in order.
|
|
101
104
|
* @throws {Error} When the note has no content at all, or repeats an anchor.
|
|
102
105
|
*/
|
|
103
|
-
export function buildPages(rawPages: Array<object>, entryId: string, noteName: string): Array<{
|
|
106
|
+
export function buildPages(rawPages: Array<object>, entryId: string, noteName: string, captions: any): Array<{
|
|
104
107
|
_id: string;
|
|
105
108
|
name: string;
|
|
106
109
|
type: string;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export function renderDot(dotPath: string, outPath: string, { engine, format, nop, dpi }: {
|
|
18
18
|
engine: "dot" | "twopi" | "neato";
|
|
19
|
-
format?: "svg" | "
|
|
19
|
+
format?: "svg" | "png" | "plain" | undefined;
|
|
20
20
|
nop?: 2 | undefined;
|
|
21
21
|
dpi?: number | undefined;
|
|
22
22
|
}): {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Find SQL helper calls, including calls nested inside Boolean expressions. */
|
|
2
|
+
export function sqlQueriesInMarkdown(body: any, fm?: {}): any[];
|
|
3
|
+
/**
|
|
4
|
+
* Expand scalar frontmatter references and registered functions in Markdown prose.
|
|
5
|
+
* @param {string} body
|
|
6
|
+
* @param {{fm?: object, dates?: object, sqlResults?: Map<string, object>, file?: string, bodyLine?: number}} [options]
|
|
7
|
+
*/
|
|
8
|
+
export function renderMarkdownExpressions(body: string, { fm, dates, sqlResults, file, bodyLine }?: {
|
|
9
|
+
fm?: object;
|
|
10
|
+
dates?: object;
|
|
11
|
+
sqlResults?: Map<string, object>;
|
|
12
|
+
file?: string;
|
|
13
|
+
bodyLine?: number;
|
|
14
|
+
}): {
|
|
15
|
+
markdown: string;
|
|
16
|
+
findings: any[];
|
|
17
|
+
};
|
|
@@ -20,8 +20,12 @@
|
|
|
20
20
|
* the month it names. A calendar that declares no list bounds nothing.
|
|
21
21
|
* @param {Map<string, object>} [options.markers] - Resolved era markers from
|
|
22
22
|
* the corpus, each carrying its calendar and canonical start.
|
|
23
|
+
* @param {Map<string, object>} [options.eras] - Resolved calendar eras, keyed
|
|
24
|
+
* by their addressed qualifier.
|
|
25
|
+
* @param {boolean} [options.ignoreEraBounds=false] - Parse a calendar row's
|
|
26
|
+
* own boundary in a referenced era without applying that era's note span.
|
|
23
27
|
* @param {number} [options.daysPerYear] - The world's year length, required for
|
|
24
|
-
* canonical days and
|
|
28
|
+
* canonical days and named dates.
|
|
25
29
|
* @param {string} [options.field] - The key that carried it, named in every
|
|
26
30
|
* message. Omitted, a message names the value alone.
|
|
27
31
|
* @param {boolean} [options.allowUnknown=true] - Whether `"unknown"` is a
|
|
@@ -39,6 +43,8 @@ export function parseNoteDate(value: unknown, options?: {
|
|
|
39
43
|
months?: readonly object[] | null;
|
|
40
44
|
} | undefined;
|
|
41
45
|
markers?: Map<string, object> | undefined;
|
|
46
|
+
eras?: Map<string, object> | undefined;
|
|
47
|
+
ignoreEraBounds?: boolean | undefined;
|
|
42
48
|
daysPerYear?: number | undefined;
|
|
43
49
|
field?: string | undefined;
|
|
44
50
|
allowUnknown?: boolean | undefined;
|
|
@@ -49,6 +55,44 @@ export function parseNoteDate(value: unknown, options?: {
|
|
|
49
55
|
date: object | null;
|
|
50
56
|
findings: object[];
|
|
51
57
|
};
|
|
58
|
+
/** Select the single era covering the full precision of a resolved date. */
|
|
59
|
+
export function eraCovering(date: any, eraRows: any, daysPerYear: any): any;
|
|
60
|
+
/** Resolve a calendar or one of its eras from an Address or shortcode. */
|
|
61
|
+
export function calendarEras(reference: any, context: any): any;
|
|
62
|
+
/** Print a resolved date using the era active in one addressed calendar. */
|
|
63
|
+
export function formatDateInCalendar(date: any, reference: any, context: any): {
|
|
64
|
+
era: null;
|
|
65
|
+
year: number;
|
|
66
|
+
text: any;
|
|
67
|
+
prose: null;
|
|
68
|
+
month?: undefined;
|
|
69
|
+
day?: undefined;
|
|
70
|
+
} | {
|
|
71
|
+
era: any;
|
|
72
|
+
year: number;
|
|
73
|
+
month: number | null;
|
|
74
|
+
day: number | null;
|
|
75
|
+
text: string;
|
|
76
|
+
prose: string | null;
|
|
77
|
+
} | null;
|
|
78
|
+
/** Print a resolved date in one era, retaining its authored precision. */
|
|
79
|
+
export function formatNoteDate(date: any, era: any, daysPerYear: any): {
|
|
80
|
+
era: null;
|
|
81
|
+
year: number;
|
|
82
|
+
text: any;
|
|
83
|
+
prose: null;
|
|
84
|
+
month?: undefined;
|
|
85
|
+
day?: undefined;
|
|
86
|
+
} | {
|
|
87
|
+
era: any;
|
|
88
|
+
year: number;
|
|
89
|
+
month: number | null;
|
|
90
|
+
day: number | null;
|
|
91
|
+
text: string;
|
|
92
|
+
prose: string | null;
|
|
93
|
+
} | null;
|
|
94
|
+
/** Normalize a note's declared dates for indexes and generated pages. */
|
|
95
|
+
export function resolvedDateFields(fm: any, context: any): {};
|
|
52
96
|
/**
|
|
53
97
|
* The literal that says a thing happened and the date is not recorded.
|
|
54
98
|
*
|
|
@@ -74,3 +118,5 @@ export const ERA_QUALIFIER_PATTERN: RegExp;
|
|
|
74
118
|
* @type {RegExp}
|
|
75
119
|
*/
|
|
76
120
|
export const NOTE_DATE_PATTERN: RegExp;
|
|
121
|
+
/** A calendar conversion written as a frontmatter date value. */
|
|
122
|
+
export const DATEFROM_PATTERN: RegExp;
|
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A
|
|
3
|
-
* though no note declares one.
|
|
4
|
-
*
|
|
5
|
-
* A `dataview` query resolves `package` out of frontmatter like any other
|
|
6
|
-
* field, so a collection note that scopes itself with `WHERE … and package =
|
|
7
|
-
* "sohl"` would match nothing now that the field is gone, and would render an
|
|
8
|
-
* **empty table** in silence. Supplying the derived value here is what kept the
|
|
9
|
-
* sweep mechanical rather than a trap — and a query that never mentions
|
|
10
|
-
* `package` is unaffected either way.
|
|
11
|
-
*
|
|
12
|
-
* The frontmatter is copied rather than written into: it is the note's own
|
|
13
|
-
* parsed object, shared with every other reader, and the derived package is a
|
|
14
|
-
* property of *this search*, not of the note.
|
|
15
|
-
*
|
|
2
|
+
* A copy of frontmatter carrying the configured package for query evaluation.
|
|
16
3
|
* @param {object|null|undefined} fm - Parsed frontmatter.
|
|
17
|
-
* @param {string} [configured] - The package this build compiles.
|
|
18
|
-
*
|
|
19
|
-
* carry it in a context object, so a caller's configuration drives every read.
|
|
20
|
-
* @returns {object|null|undefined} A shallow copy carrying the derived package,
|
|
21
|
-
* or whatever was passed when it is not frontmatter at all.
|
|
4
|
+
* @param {string} [configured] - The package this build compiles.
|
|
5
|
+
* @returns {object|null|undefined} Frontmatter with a derived package value.
|
|
22
6
|
*/
|
|
23
7
|
export function searchableFrontmatter(fm: object | null | undefined, configured?: string): object | null | undefined;
|
|
24
8
|
/**
|
|
@@ -111,7 +111,8 @@ export function placeholderBody(body: string | null | undefined): Array<{
|
|
|
111
111
|
*
|
|
112
112
|
* Fence blocks, headings, thematic breaks and HTML comments are dropped, and a
|
|
113
113
|
* wikilink counts as the one word it renders — `[[affiliation-meivor|Mëivōr]]`
|
|
114
|
-
* is a word, not three.
|
|
114
|
+
* is a word, not three. A dash, slash, or ellipsis between words separates
|
|
115
|
+
* them even without surrounding spaces.
|
|
115
116
|
*
|
|
116
117
|
* @param {string|null|undefined} body - The note body.
|
|
117
118
|
* @returns {number} The word count.
|
|
@@ -61,6 +61,8 @@ export function hasTag(fm: object | null | undefined, tag: string): boolean;
|
|
|
61
61
|
* @returns {boolean} Whether the note carries the `draft` tag.
|
|
62
62
|
*/
|
|
63
63
|
export function isDraftNote(fm: object | null | undefined): boolean;
|
|
64
|
+
/** Whether a note is restricted to GM-facing output. */
|
|
65
|
+
export function isGmNote(fm: any): boolean;
|
|
64
66
|
/**
|
|
65
67
|
* What a note carrying a subType outside the address charset is told.
|
|
66
68
|
*
|
|
@@ -167,6 +169,8 @@ export const SHARED_DATA_FIELDS: readonly DataFieldSpec[];
|
|
|
167
169
|
* {@link draftRetiredMessage}.
|
|
168
170
|
*/
|
|
169
171
|
export const DRAFT_TAG: "draft";
|
|
172
|
+
/** A note intended for the GM rather than the player-facing outputs. */
|
|
173
|
+
export const GM_TAG: "gm";
|
|
170
174
|
/**
|
|
171
175
|
* The tags that **classify** a note, grouped by what they classify.
|
|
172
176
|
*
|
|
@@ -243,6 +247,11 @@ export const DECLARED_TAGS: Readonly<{
|
|
|
243
247
|
types: null;
|
|
244
248
|
tags: readonly string[];
|
|
245
249
|
}>;
|
|
250
|
+
/** Who may read a completed note. */
|
|
251
|
+
audience: Readonly<{
|
|
252
|
+
types: null;
|
|
253
|
+
tags: readonly string[];
|
|
254
|
+
}>;
|
|
246
255
|
}>;
|
|
247
256
|
/**
|
|
248
257
|
* Every note type this toolchain compiles, and the closed vocabulary it
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate one affiliation's office roster against dated beings in the corpus.
|
|
3
|
+
* @param {object} note
|
|
4
|
+
* @param {{index?: object}} [options]
|
|
5
|
+
*/
|
|
6
|
+
export function checkDatedOffices(note: object, { index }?: {
|
|
7
|
+
index?: object;
|
|
8
|
+
}): {
|
|
9
|
+
severity: string;
|
|
10
|
+
message: any;
|
|
11
|
+
line?: number;
|
|
12
|
+
column?: number;
|
|
13
|
+
file: any;
|
|
14
|
+
}[];
|
|
@@ -137,7 +137,7 @@ export const CONFIG_BASENAME: "package-build.config";
|
|
|
137
137
|
* the order the names are *reported* in, which is the order a reader should
|
|
138
138
|
* reach for them: YAML first, `.mjs` last.
|
|
139
139
|
*
|
|
140
|
-
* The `
|
|
140
|
+
* The `package-build.config.*` stem this package read before the two toolchains
|
|
141
141
|
* merged is **not** resolved. 3.0.0 renames the file rather than accepting both:
|
|
142
142
|
* a deprecation window here would mean a repository could sit indefinitely on a
|
|
143
143
|
* name for a package that no longer exists, and the upgrade already requires
|
|
@@ -40,8 +40,9 @@ export function isReservedPackage(pkg: unknown): boolean;
|
|
|
40
40
|
* with, which is the whole point: the alternative is every package depending on
|
|
41
41
|
* one of the others just to borrow a banner.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
44
|
-
* resolution is special-cased
|
|
43
|
+
* Only this toolchain's own documentation package may share the name. Other
|
|
44
|
+
* repositories cannot claim it, and asset resolution is special-cased because
|
|
45
|
+
* no installed Foundry directory sits behind it.
|
|
45
46
|
*
|
|
46
47
|
* This module is a **leaf with no local imports**, so the configuration
|
|
47
48
|
* validator can name it without closing a cycle around `content-config.mjs`.
|