@heroiclands/package-build 23.8.0 → 23.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +2 -0
  3. package/bin/content-commands.mjs +207 -70
  4. package/docs/api.md +646 -78
  5. package/docs/authoring/first-note.md +1 -1
  6. package/docs/authoring/links-and-markup.md +253 -102
  7. package/docs/commands.md +111 -13
  8. package/docs/content-format.md +7 -2
  9. package/docs/diagnostics.md +1 -1
  10. package/docs/guides/packs.md +2 -1
  11. package/docs/guides/reference-pages.md +86 -63
  12. package/docs/reference/format-details.md +398 -137
  13. package/docs/reference/note-types.md +188 -174
  14. package/engine/anchors.mjs +15 -26
  15. package/engine/base-compiler.mjs +6 -4
  16. package/engine/changelog-group.mjs +58 -34
  17. package/engine/changelog-lint.mjs +6 -2
  18. package/engine/compendiums.mjs +15 -1
  19. package/engine/compile-corpus.mjs +17 -3
  20. package/engine/content-charset.mjs +13 -1
  21. package/engine/content-format.yaml +1 -0
  22. package/engine/content-html.mjs +18 -4
  23. package/engine/content-icons.mjs +15 -1
  24. package/engine/content-images.mjs +28 -9
  25. package/engine/content-index.mjs +496 -16
  26. package/engine/content-links.mjs +70 -27
  27. package/engine/content-session.mjs +247 -0
  28. package/engine/content-sources.mjs +110 -0
  29. package/engine/content-spans.mjs +87 -8
  30. package/engine/content-tables.mjs +9 -4
  31. package/engine/content-tasklists.mjs +20 -4
  32. package/engine/culture-reach.mjs +61 -34
  33. package/engine/data-keys.mjs +3 -1
  34. package/engine/diagnostics.mjs +17 -6
  35. package/engine/dictionary-layout.mjs +151 -19
  36. package/engine/dictionary-rows.mjs +129 -79
  37. package/engine/extension-attributes.mjs +28 -5
  38. package/engine/first-use.mjs +371 -0
  39. package/engine/foundry-entries.mjs +29 -8
  40. package/engine/generate.mjs +13 -4
  41. package/engine/glossary-entries.mjs +11 -11
  42. package/engine/heading-attributes.mjs +21 -28
  43. package/engine/helpers.mjs +54 -19
  44. package/engine/index-records.mjs +1 -0
  45. package/engine/index.mjs +3 -0
  46. package/engine/journals.mjs +13 -6
  47. package/engine/language-data.mjs +55 -2
  48. package/engine/language-rows.mjs +4 -4
  49. package/engine/language-terms.mjs +90 -4
  50. package/engine/language-words.mjs +2 -2
  51. package/engine/lexical-sections.mjs +247 -0
  52. package/engine/markup-validation.mjs +7 -2
  53. package/engine/metadata-index.mjs +1 -0
  54. package/engine/name-markup.mjs +114 -0
  55. package/engine/name-rows.mjs +70 -0
  56. package/engine/names.mjs +460 -0
  57. package/engine/note-addresses.mjs +25 -4
  58. package/engine/note-claims.mjs +2 -0
  59. package/engine/note-state.mjs +9 -3
  60. package/engine/note-vocabulary.mjs +7 -6
  61. package/engine/path-charset.mjs +24 -1
  62. package/engine/pdf-build.mjs +9 -9
  63. package/engine/pdf-render.mjs +15 -1
  64. package/engine/site-build.mjs +16 -2
  65. package/engine/site-index.mjs +5 -1
  66. package/engine/sql-tables.mjs +324 -44
  67. package/engine/stub-lint.mjs +10 -5
  68. package/engine/web-wikilinks.mjs +8 -6
  69. package/engine/word-rows.mjs +20 -12
  70. package/engine/word-terms.mjs +15 -6
  71. package/engine/yaml-lint.mjs +3 -5
  72. package/hugo-theme/static/css/style.css +4 -0
  73. package/package.json +1 -1
  74. package/types/engine/anchors.d.mts +4 -2
  75. package/types/engine/compendiums.d.mts +5 -1
  76. package/types/engine/compile-corpus.d.mts +5 -2
  77. package/types/engine/content-charset.d.mts +5 -1
  78. package/types/engine/content-html.d.mts +5 -1
  79. package/types/engine/content-icons.d.mts +5 -1
  80. package/types/engine/content-images.d.mts +5 -1
  81. package/types/engine/content-index.d.mts +102 -18
  82. package/types/engine/content-links.d.mts +22 -8
  83. package/types/engine/content-session.d.mts +108 -0
  84. package/types/engine/content-sources.d.mts +32 -0
  85. package/types/engine/content-spans.d.mts +3 -5
  86. package/types/engine/content-tables.d.mts +2 -1
  87. package/types/engine/content-tasklists.d.mts +5 -1
  88. package/types/engine/culture-reach.d.mts +22 -2
  89. package/types/engine/data-keys.d.mts +7 -0
  90. package/types/engine/diagnostics.d.mts +10 -0
  91. package/types/engine/dictionary-layout.d.mts +35 -4
  92. package/types/engine/dictionary-rows.d.mts +9 -0
  93. package/types/engine/extension-attributes.d.mts +16 -2
  94. package/types/engine/first-use.d.mts +31 -0
  95. package/types/engine/foundry-entries.d.mts +14 -5
  96. package/types/engine/generate.d.mts +5 -1
  97. package/types/engine/heading-attributes.d.mts +2 -2
  98. package/types/engine/helpers.d.mts +33 -7
  99. package/types/engine/index.d.mts +1 -0
  100. package/types/engine/journals.d.mts +5 -2
  101. package/types/engine/language-terms.d.mts +7 -0
  102. package/types/engine/lexical-sections.d.mts +96 -0
  103. package/types/engine/markup-validation.d.mts +2 -1
  104. package/types/engine/name-markup.d.mts +176 -0
  105. package/types/engine/name-rows.d.mts +35 -0
  106. package/types/engine/names.d.mts +356 -0
  107. package/types/engine/note-addresses.d.mts +3 -3
  108. package/types/engine/note-state.d.mts +2 -1
  109. package/types/engine/path-charset.d.mts +16 -1
  110. package/types/engine/site-build.d.mts +6 -3
  111. package/types/engine/site-index.d.mts +3 -1
  112. package/types/engine/sql-tables.d.mts +39 -4
  113. package/types/engine/stub-lint.d.mts +3 -1
  114. package/types/engine/word-rows.d.mts +9 -7
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Capture one source tree for index, lint, links and compilation.
3
+ * No source or parser state is persisted or reused by another invocation.
4
+ * Prepared SQL databases close within their existing preparation functions.
5
+ * @param {object} [options]
6
+ * @param {import("../content-config.mjs").ContentBuildConfig} [options.config] - Exact resolved configuration.
7
+ * @param {string} [options.contentBase] - Content root, defaulting to configuration.
8
+ * @param {string} [options.assetsBase] - Assets root, defaulting to configuration.
9
+ * @param {readonly string[]} [options.skipDirectories] - Resolved content scope.
10
+ * @param {ReturnType<typeof routerFor>} [options.router] - One router shared by this session's compile.
11
+ * @returns {Promise<ContentSession>} Captured sources and lazy prepared views; dispose after use.
12
+ */
13
+ export function createContentSession({ config, contentBase, assetsBase, skipDirectories, router, }?: {
14
+ config?: import("../content-config.mjs").ContentBuildConfig | undefined;
15
+ contentBase?: string | undefined;
16
+ assetsBase?: string | undefined;
17
+ skipDirectories?: readonly string[] | undefined;
18
+ router?: {
19
+ resolve: (fm: object, docType: string, system?: string) => string | undefined;
20
+ resolveOrNull: (fm: object, docType: string, system?: string) => string | undefined;
21
+ packsOfType: (docType: string) => string[];
22
+ defaultOf: (docType: string) => string | undefined;
23
+ } | undefined;
24
+ }): Promise<ContentSession>;
25
+ /**
26
+ * @typedef {"index"|"lint"|"links"|"compile"} ContentTarget
27
+ * @typedef {(source: string) => {blocks: ReturnType<typeof blocks.scanBlocks>, alerts: ReturnType<typeof alerts.scanAlerts>, spans: ReturnType<typeof spans.scanSpans>}} ContentSyntax
28
+ * @typedef {{config?: import("../content-config.mjs").ContentBuildConfig, contentBase?: string, assetsBase?: string, skipDirectories?: readonly string[], router?: ReturnType<typeof routerFor>}} ContentSessionContext
29
+ * @typedef {{frontmatter: object|null, body: string, raw: string, bodyLine?: number, bodyColumn?: number}} ContentNoteSource
30
+ * @typedef {object} ContentSession
31
+ * @property {import("../content-config.mjs").ContentBuildConfig} config
32
+ * @property {string} contentBase
33
+ * @property {string} assetsBase
34
+ * @property {readonly string[]} skipDirectories
35
+ * @property {ReturnType<typeof routerFor>} router
36
+ * @property {ReturnType<typeof captureContentSources>} inventory
37
+ * @property {Map<string, {raw: string}>} sources
38
+ * @property {object[]} records
39
+ * @property {Map<string, ContentNoteSource>} parsed
40
+ * @property {object[]} problems
41
+ * @property {ReturnType<typeof loadForeignIndexes>} foreign
42
+ * @property {ContentSyntax} syntax
43
+ * @property {(options?: ContentSessionContext) => void} assertContext
44
+ * @property {(audience?: "all"|"public") => ReturnType<typeof sql.prepareTreeSqlTables>} sqlTables
45
+ * @property {() => ReturnType<typeof sql.prepareLinkSqlTables>} linkTables
46
+ * @property {(kind?: "lint"|"links") => Promise<ReturnType<typeof buildLinkIndex>>} audit
47
+ * @property {() => Promise<import("./content-index.mjs").PreparedContentIndex>} publication
48
+ * @property {() => ReturnType<import("./compile-corpus.mjs").buildCompileCorpus>} compileCorpus
49
+ * @property {(targets: readonly ContentTarget[]) => Promise<ContentSession>} prepare
50
+ * @property {() => void} dispose
51
+ */
52
+ /**
53
+ * The explicit runner's target vocabulary and execution order.
54
+ * @type {readonly ContentTarget[]}
55
+ */
56
+ export const CONTENT_TARGETS: readonly ContentTarget[];
57
+ export type ContentTarget = "index" | "lint" | "links" | "compile";
58
+ export type ContentSyntax = (source: string) => {
59
+ blocks: ReturnType<typeof blocks.scanBlocks>;
60
+ alerts: ReturnType<typeof alerts.scanAlerts>;
61
+ spans: ReturnType<typeof spans.scanSpans>;
62
+ };
63
+ export type ContentSessionContext = {
64
+ config?: import("../content-config.mjs").ContentBuildConfig;
65
+ contentBase?: string;
66
+ assetsBase?: string;
67
+ skipDirectories?: readonly string[];
68
+ router?: ReturnType<typeof routerFor>;
69
+ };
70
+ export type ContentNoteSource = {
71
+ frontmatter: object | null;
72
+ body: string;
73
+ raw: string;
74
+ bodyLine?: number;
75
+ bodyColumn?: number;
76
+ };
77
+ export type ContentSession = {
78
+ config: import("../content-config.mjs").ContentBuildConfig;
79
+ contentBase: string;
80
+ assetsBase: string;
81
+ skipDirectories: readonly string[];
82
+ router: ReturnType<typeof routerFor>;
83
+ inventory: ReturnType<typeof captureContentSources>;
84
+ sources: Map<string, {
85
+ raw: string;
86
+ }>;
87
+ records: object[];
88
+ parsed: Map<string, ContentNoteSource>;
89
+ problems: object[];
90
+ foreign: ReturnType<typeof loadForeignIndexes>;
91
+ syntax: ContentSyntax;
92
+ assertContext: (options?: ContentSessionContext) => void;
93
+ sqlTables: (audience?: "all" | "public") => ReturnType<typeof sql.prepareTreeSqlTables>;
94
+ linkTables: () => ReturnType<typeof sql.prepareLinkSqlTables>;
95
+ audit: (kind?: "lint" | "links") => Promise<ReturnType<typeof buildLinkIndex>>;
96
+ publication: () => Promise<import("./content-index.mjs").PreparedContentIndex>;
97
+ compileCorpus: () => ReturnType<typeof import("./compile-corpus.mjs").buildCompileCorpus>;
98
+ prepare: (targets: readonly ContentTarget[]) => Promise<ContentSession>;
99
+ dispose: () => void;
100
+ };
101
+ import * as blocks from "./content-blocks.mjs";
102
+ import * as alerts from "./content-alerts.mjs";
103
+ import * as spans from "./content-spans.mjs";
104
+ import { routerFor } from "./pack-router.mjs";
105
+ import { captureContentSources } from "./content-sources.mjs";
106
+ import { loadForeignIndexes } from "./metadata-index.mjs";
107
+ import * as sql from "./sql-tables.mjs";
108
+ import { buildLinkIndex } from "./content-links.mjs";
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Capture exact text once and retain directory entries for physical checks.
3
+ * The index walk and raw check walks retain their established traversal orders.
4
+ * @param {string} contentBase - Absolute content root.
5
+ * @param {string} assetsBase - Absolute assets root.
6
+ * @param {readonly string[]} skipDirectories - Resolved directory scope.
7
+ * @returns {{sources: Map<string, {raw: string}>, indexSources: Map<string, {raw: string}>, paths: Array<{root: string, file: string, name: string, directory: boolean}>}} Captured input and physical walk order.
8
+ */
9
+ export function captureContentSources(contentBase: string, assetsBase: string, skipDirectories: readonly string[]): {
10
+ sources: Map<string, {
11
+ raw: string;
12
+ }>;
13
+ indexSources: Map<string, {
14
+ raw: string;
15
+ }>;
16
+ paths: Array<{
17
+ root: string;
18
+ file: string;
19
+ name: string;
20
+ directory: boolean;
21
+ }>;
22
+ };
23
+ /**
24
+ * Select the exact physical scope of a raw checker from a captured map.
25
+ * @param {string} contentBase - Content root.
26
+ * @param {Map<string, {raw: string}>} sources - Captured physical sources.
27
+ * @param {readonly string[]} skipDirectories - Directory names to ignore.
28
+ * @param {Set<string>} extensions - Lowercase extensions accepted by the caller.
29
+ */
30
+ export function capturedSources(contentBase: string, sources: Map<string, {
31
+ raw: string;
32
+ }>, skipDirectories: readonly string[], extensions: Set<string>): Generator<string[], void, unknown>;
@@ -4,15 +4,13 @@
4
4
  export function spanMarkdownPlugin(md: typeof import("markdown-it")): void;
5
5
  /** Read span anchors and located attribute diagnostics outside literal code.
6
6
  * @param {string} source - Authored Markdown.
7
+ * @returns {import("./name-markup.mjs").SpanScan} Actual spans, explicit names and located diagnostics.
7
8
  */
8
- export function scanSpans(source: string): {
9
- spans: any[];
10
- errors: any[];
11
- };
9
+ export function scanSpans(source: string): import("./name-markup.mjs").SpanScan;
12
10
  /** Emit span tags while leaving their inline content as Markdown for Hugo.
13
11
  * @param {string} source - Authored Markdown.
14
12
  */
15
13
  export function renderSpans(source: string): {
16
14
  markdown: string;
17
- errors: any[];
15
+ errors: import("./name-markup.mjs").SpanFinding[];
18
16
  };
@@ -12,7 +12,8 @@
12
12
  * Prepared results.
13
13
  * A note's generated sections — see {@link module:engine/generated-sections} —
14
14
  * are appended to the body first, from the `generated` text the preparation
15
- * set on its results, and expanded with it.
15
+ * set on its results, and expanded with it. Automatic lexical lists, carried
16
+ * on `lexical`, then fill their authored sections or append a new section.
16
17
  *
17
18
  * @returns {{markdown: string, errors: object[], warnings: object[], lineMap: object[],
18
19
  * generatedFrom?: number}} `generatedFrom` is the 0-based line of the
@@ -43,12 +43,16 @@ export function checkTaskLists(body: string, file: string, { bodyLine, bodyColum
43
43
  * @param {object} [opts]
44
44
  * @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore
45
45
  * in addition to the dot-directories always skipped.
46
+ * @param {Map<string, {raw: string}>} [opts.sources] - Exact captured source strings; omitted, walks and reads the tree.
46
47
  * @returns {{findings: Array<{file: string, line: number, column: number,
47
48
  * severity: "warning", message: string}>, files: number}} The findings, and
48
49
  * how many files were read.
49
50
  */
50
- export function lintContentTaskLists(contentBase: string, { skipDirectories }?: {
51
+ export function lintContentTaskLists(contentBase: string, { skipDirectories, sources }?: {
51
52
  skipDirectories?: readonly string[] | undefined;
53
+ sources?: Map<string, {
54
+ raw: string;
55
+ }> | undefined;
52
56
  }): {
53
57
  findings: Array<{
54
58
  file: string;
@@ -32,12 +32,15 @@ export function glossaryCorpus(index: object): GlossaryCorpus;
32
32
  *
33
33
  * @param {object} index - The link index.
34
34
  * @param {object} note - The culture or affiliation note.
35
+ * @param {object} [entry] - A glossary entry, whose resolved language selects
36
+ * its dictionary words. Absent, the note's own language is used.
35
37
  * @returns {Map<string, string[]>} The terms; empty for a note no source reaches.
36
38
  */
37
- export function noteTerms(index: object, note: object): Map<string, string[]>;
39
+ export function noteTerms(index: object, note: object, entry?: object): Map<string, string[]>;
38
40
  /**
39
41
  * Warn of an affiliation that reaches no culture, its own or one it inherits,
40
- * in a tree that describes peoples.
42
+ * in a tree that describes peoples. Its ranks and offices then have no
43
+ * culture associations for term queries.
41
44
  *
42
45
  * @param {object} note - An `affiliation` note.
43
46
  * @param {{index?: object}} [context]
@@ -63,4 +66,21 @@ export type GlossaryCorpus = {
63
66
  * give it.
64
67
  */
65
68
  terms: Map<string, Map<string, string[]>>;
69
+ /**
70
+ * - The ranks and
71
+ * offices each owner can name.
72
+ */
73
+ governance: Map<string, Map<string, string[]>>;
74
+ /**
75
+ * - Each
76
+ * language's dictionary words.
77
+ */
78
+ words: Map<string, Array<{
79
+ form: string;
80
+ gloss: unknown;
81
+ }>>;
82
+ /**
83
+ * - Each language's parent.
84
+ */
85
+ families: Map<string, string | undefined>;
66
86
  };
@@ -13,6 +13,8 @@
13
13
  * every surface shows it exactly as written, never recased.
14
14
  * @property {string} shape - The shape in words, for a finding and the reference.
15
15
  * @property {boolean} [required] - The key must be stated, with a value.
16
+ * @property {boolean} [allowEmpty] - A required string may be empty where
17
+ * empty text is a stated value.
16
18
  * @property {boolean} [nullable] - An explicit `null` states the key, for a
17
19
  * required key whose null has a meaning of its own.
18
20
  * @property {string} [label] - The infobox row label, where humanising the key
@@ -117,6 +119,11 @@ export type InnerKeySpec = {
117
119
  * - The key must be stated, with a value.
118
120
  */
119
121
  required?: boolean | undefined;
122
+ /**
123
+ * - A required string may be empty where
124
+ * empty text is a stated value.
125
+ */
126
+ allowEmpty?: boolean | undefined;
120
127
  /**
121
128
  * - An explicit `null` states the key, for a
122
129
  * required key whose null has a meaning of its own.
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Split opening YAML frontmatter from its untrimmed body without parsing it.
3
+ * LF and CRLF delimiters retain the exact source strings and their offsets.
4
+ * @param {string} raw - Complete Markdown source.
5
+ * @returns {{yaml: string, body: string}|null} Source slices, or no fence.
6
+ */
7
+ export function splitFrontmatter(raw: string): {
8
+ yaml: string;
9
+ body: string;
10
+ } | null;
1
11
  /**
2
12
  * The `file:line:column` locator, with whatever is known.
3
13
  *
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Render a dictionary entry's marked headword as an inline native heading in
3
+ * Foundry. Its paragraph becomes a div, so the heading has a valid container.
4
+ * The shared Markdown keeps its span and its containing journal page.
5
+ *
6
+ * @param {import("markdown-it")} md - The Foundry Markdown parser.
7
+ */
8
+ export function dictionaryEntryPlugin(md: typeof import("markdown-it")): void;
1
9
  /**
2
10
  * @typedef {object} LayoutOptions
3
11
  * @property {(ref: object) => boolean} [linkable] - Whether an Address can be
@@ -6,10 +14,16 @@
6
14
  * @property {object} [addressContext] - Address resolution context.
7
15
  * @property {(address: string) => string|undefined} [labelOf] - A note's
8
16
  * `name.full`, by its Address.
9
- * @property {boolean|"only"} [showKey=true] - Whether the key is printed;
10
- * `"only"` prints the key and nothing else.
17
+ * @property {boolean|"only"} [showKey=true] - Whether the key and dictionary
18
+ * head tables are printed; `"only"` prints the key and nothing else.
11
19
  * @property {string} [pageLanguage] - The language of the page the fence is
12
20
  * on, which no entry is tagged with.
21
+ * @property {string} [pageOwner] - The containing page's canonical note Address;
22
+ * a sense attributed only to its own row's owner omits that label on this page.
23
+ * @property {boolean} [entryAnchors=true] - Whether dictionary headwords carry
24
+ * their canonical targets; custom SQL views leave those to the automatic list.
25
+ * @property {object} [verbs] - The owning language's stated pattern, including
26
+ * when its automatic dictionary has no eligible word entries.
13
27
  */
14
28
  /**
15
29
  * Render a result of words as a dictionary.
@@ -44,6 +58,8 @@ export const LAYOUT_REQUIRED_COLUMNS: Readonly<{
44
58
  }>;
45
59
  /** The mark a common entry opens with. */
46
60
  export const COMMON_MARK: "\u2022";
61
+ /** The word-entry span class whose Foundry rendering is a native TOC heading. */
62
+ export const DICTIONARY_ENTRY_CLASS: "dictionary-entry";
47
63
  export type LayoutOptions = {
48
64
  /**
49
65
  * - Whether an Address can be
@@ -64,8 +80,8 @@ export type LayoutOptions = {
64
80
  */
65
81
  labelOf?: ((address: string) => string | undefined) | undefined;
66
82
  /**
67
- * - Whether the key is printed;
68
- * `"only"` prints the key and nothing else.
83
+ * - Whether the key and dictionary
84
+ * head tables are printed; `"only"` prints the key and nothing else.
69
85
  */
70
86
  showKey?: boolean | "only" | undefined;
71
87
  /**
@@ -73,4 +89,19 @@ export type LayoutOptions = {
73
89
  * on, which no entry is tagged with.
74
90
  */
75
91
  pageLanguage?: string | undefined;
92
+ /**
93
+ * - The containing page's canonical note Address;
94
+ * a sense attributed only to its own row's owner omits that label on this page.
95
+ */
96
+ pageOwner?: string | undefined;
97
+ /**
98
+ * - Whether dictionary headwords carry
99
+ * their canonical targets; custom SQL views leave those to the automatic list.
100
+ */
101
+ entryAnchors?: boolean | undefined;
102
+ /**
103
+ * - The owning language's stated pattern, including
104
+ * when its automatic dictionary has no eligible word entries.
105
+ */
106
+ verbs?: object | undefined;
76
107
  };
@@ -1,3 +1,12 @@
1
+ /**
2
+ * The stable prose anchor of a word in one language's dictionary. The exact
3
+ * language and NFC form identify it, including case and accents.
4
+ *
5
+ * @param {string} language - The language's canonical note Address.
6
+ * @param {string} name - The dictionary headword.
7
+ * @returns {string} The anchor slug.
8
+ */
9
+ export function dictionaryEntryAnchor(language: string, name: string): string;
1
10
  /**
2
11
  * The heading letter of a sort key: its first letter or digit, accents taken
3
12
  * off and apostrophes skipped, in capitals; `#` where it has none.
@@ -6,15 +6,29 @@
6
6
  * @param {RegExp} [options.idPattern=NAME] - What an `#id` may be named. A
7
7
  * construct whose id is published as an HTML `id` admits a digit first and
8
8
  * passes a looser pattern; the default is the pattern every other name takes.
9
- * @returns {{id: string, classes: string[], values: Record<string, string>, problems: string[]}}
9
+ * @param {boolean} [options.positions=false] - Retain the first supplied
10
+ * attribute's key/value ranges as UTF-16 offsets within this input.
11
+ * @returns {{id: string, classes: string[], values: Record<string, string>, problems: string[],
12
+ * positions?: Record<string, {key: {start: number, end: number}, value: {start: number, end: number}}>}}
10
13
  */
11
- export function parseExtensionAttributes(source: string, { idPattern }?: {
14
+ export function parseExtensionAttributes(source: string, { idPattern, positions }?: {
12
15
  idPattern?: RegExp | undefined;
16
+ positions?: boolean | undefined;
13
17
  }): {
14
18
  id: string;
15
19
  classes: string[];
16
20
  values: Record<string, string>;
17
21
  problems: string[];
22
+ positions?: Record<string, {
23
+ key: {
24
+ start: number;
25
+ end: number;
26
+ };
27
+ value: {
28
+ start: number;
29
+ end: number;
30
+ };
31
+ }>;
18
32
  };
19
33
  /** A Boolean attribute is spelled exactly `true` or `false`. */
20
34
  export function booleanAttribute(value: any, key: any): boolean;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Prepare indexed names once, then check only explicit occurrences without reading files.
3
+ * @param {{records: readonly object[], index: object,
4
+ * catalog?: import("./names.mjs").NameCatalog,
5
+ * parsed?: Map<string, {raw: string, body: string, bodyLine?: number, bodyColumn?: number}>,
6
+ * syntax?: import("./content-session.mjs").ContentSyntax}} options - Lint's
7
+ * records/resolver and optional captured authored-body projections.
8
+ * @returns {(note: {file: string, raw: string}) => Array<{file: string, line: number, column: number, severity: "warning", rules: string[], message: string}>} Located warning diagnostics.
9
+ */
10
+ export function createFirstUseChecker({ records, index, parsed, syntax, catalog, }: {
11
+ records: readonly object[];
12
+ index: object;
13
+ catalog?: import("./names.mjs").NameCatalog;
14
+ parsed?: Map<string, {
15
+ raw: string;
16
+ body: string;
17
+ bodyLine?: number;
18
+ bodyColumn?: number;
19
+ }>;
20
+ syntax?: import("./content-session.mjs").ContentSyntax;
21
+ }): (note: {
22
+ file: string;
23
+ raw: string;
24
+ }) => Array<{
25
+ file: string;
26
+ line: number;
27
+ column: number;
28
+ severity: "warning";
29
+ rules: string[];
30
+ message: string;
31
+ }>;
@@ -1,5 +1,7 @@
1
1
  /**
2
- * Every page of a note's journal, as `anchorName → whole UUID`.
2
+ * Every anchor of a note's journal, as `anchorName → Foundry target`.
3
+ * A dictionary word's native heading carries its fragment after the
4
+ * containing page's UUID.
3
5
  *
4
6
  * Whole, not a fragment appended to the entry's UUID: nothing owns a page
5
7
  * address, so a complete link restates no fact, and it keeps the page-id hash
@@ -11,9 +13,10 @@
11
13
  * @param {string} entryId - The entry's id, which page ids hash against.
12
14
  * @param {string} body - The note's markdown body.
13
15
  * @param {string} name - The note's name, used as the lead page's title.
16
+ * @param {import("./content-session.mjs").ContentSyntax} [syntax] - Session scanner projections.
14
17
  * @returns {Record<string, string>} The anchors.
15
18
  */
16
- export function anchorsOf(entryUuid: string, entryId: string, body: string, name: string): Record<string, string>;
19
+ export function anchorsOf(entryUuid: string, entryId: string, body: string, name: string, syntax?: import("./content-session.mjs").ContentSyntax): Record<string, string>;
17
20
  /**
18
21
  * The manifest entries a single note produces.
19
22
  *
@@ -35,11 +38,17 @@ export function anchorsOf(entryUuid: string, entryId: string, body: string, name
35
38
  * documentation journal's is {@link itemDocEntryId} of that. Stating it here is
36
39
  * what lets the content index publish an identity it did not re-derive.
37
40
  *
38
- * @param {object} ctx - Resolved identities: `{ contentPackage,
39
- * foundryPackageId, packRouter }`.
41
+ * @param {{contentPackage: string, foundryPackageId: string, packRouter: object, web?: boolean, skipDirectories?: readonly string[], syntax?: import("./content-session.mjs").ContentSyntax}} ctx - Resolved identities and optional session scanner projections.
40
42
  * @returns {Array<object>} One or two entries, in {@link buildManifest}'s shape.
41
43
  */
42
- export function entriesForNote(fm: object, name: string, address: string, body: string, ctx: object): Array<object>;
44
+ export function entriesForNote(fm: object, name: string, address: string, body: string, ctx: {
45
+ contentPackage: string;
46
+ foundryPackageId: string;
47
+ packRouter: object;
48
+ web?: boolean;
49
+ skipDirectories?: readonly string[];
50
+ syntax?: import("./content-session.mjs").ContentSyntax;
51
+ }): Array<object>;
43
52
  /**
44
53
  * Every note this package publishes, as manifest entries.
45
54
  *
@@ -201,12 +201,16 @@ export function emptyPassErrors(passes: Array<{
201
201
  * package's tree — and how the guard-order test below induces id drift, now
202
202
  * that the manifest is located by configuration rather than by the working
203
203
  * directory.
204
+ * @param {import("./content-session.mjs").ContentSession} [opts.session] - Same configuration, root and scope as this generation.
205
+ * @param {boolean} [opts.reportPreparation=true] - Emit shared preparation findings; false when already reported by the caller.
204
206
  * @returns {Promise<number>} Total error count across the generated packs.
205
207
  * @throws {Error} If the configured Foundry package id has drifted from the
206
208
  * shipped manifest's `id` (see `package-manifest.mjs`).
207
209
  */
208
- export function generatePacksJson({ only, config }?: {
210
+ export function generatePacksJson({ only, config, session, reportPreparation, }?: {
209
211
  only?: string | undefined;
210
212
  config?: object | undefined;
213
+ session?: import("./content-session.mjs").ContentSession | undefined;
214
+ reportPreparation?: boolean | undefined;
211
215
  }): Promise<number>;
212
216
  export function packJsonDir(name: string, config?: object): string;
@@ -46,8 +46,8 @@ export function opensPage({ level, id }: {
46
46
  /**
47
47
  * Every heading in a body, with what its attribute block declares.
48
48
  *
49
- * Fenced code is skipped: `#` opens a comment in most of what gets fenced, and
50
- * an example heading in a document is not a heading of it.
49
+ * Code and raw HTML blocks are skipped: an example heading in a document is
50
+ * not a heading of it, including an example in an HTML comment.
51
51
  *
52
52
  * @param {string} source - A note's markdown body.
53
53
  * @param {number} [bodyLine=1] - The 1-based file line the body starts on, so a
@@ -17,7 +17,7 @@ export function renderFoundryMarkdown(body: string, figures?: Array<{
17
17
  label: string;
18
18
  }>, footnoteNumbers?: Map<string, number>, docId?: string, resolveRole?: (address: string) => string | undefined): string;
19
19
  /**
20
- * Parses a markdown file with YAML frontmatter.
20
+ * Parses a markdown file with LF or CRLF YAML frontmatter delimiters.
21
21
  *
22
22
  * Returns `{ frontmatter, body, description, bodyLine, bodyColumn, raw }` where
23
23
  * `body` is the trimmed raw markdown after the frontmatter block, and
@@ -29,13 +29,19 @@ export function renderFoundryMarkdown(body: string, figures?: Array<{
29
29
  * `{ frontmatter: null, body: "", description: "", raw }` and no
30
30
  * position: there is no body to have one.
31
31
  * @param {string} filePath - Markdown file to read.
32
- * @param {{addressContext?: object}} [opts] - Address defaults for decoded fields.
32
+ * @param {object} [opts]
33
+ * @param {object} [opts.addressContext] - Address defaults for decoded fields.
34
+ * @param {string} [opts.raw] - Exact captured source; omitted, reads the file.
35
+ * @param {boolean} [opts.renderDescription=true] - Render the body's HTML description.
36
+ * False leaves `description` empty for readers that only use authored text.
33
37
  * @returns {{frontmatter: object|null, body: string, description: string, raw: string,
34
38
  * bodyLine?: number, bodyColumn?: number, parseError?: {message: string,
35
39
  * line?: number, column?: number}}} Parsed note and body position.
36
40
  */
37
- export function parseMarkdownFile(filePath: string, { addressContext }?: {
38
- addressContext?: object;
41
+ export function parseMarkdownFile(filePath: string, { addressContext, raw: capturedRaw, renderDescription }?: {
42
+ addressContext?: object | undefined;
43
+ raw?: string | undefined;
44
+ renderDescription?: boolean | undefined;
39
45
  }): {
40
46
  frontmatter: object | null;
41
47
  body: string;
@@ -105,17 +111,24 @@ export function assertSuppliedCorpus(records: readonly object[] | undefined, who
105
111
  * Required: the scope is the caller's to state, so two passes cannot
106
112
  * disagree about which files are the corpus.
107
113
  * @param {object} [opts.addressContext] - Address resolution context.
114
+ * @param {Map<string, {raw: string}>} [opts.sources] - Selected sources in walk order.
115
+ * Supplied, the walk parses these strings instead of reading the filesystem.
116
+ * @param {boolean} [opts.renderDescription=true] - Render each body's description.
108
117
  * @yields {{frontmatter: object|null, body: string, description: string,
109
118
  * file: string, absPath: string, bodyLine?: number, bodyColumn?: number}}
110
119
  * One entry per `.md` file found.
111
120
  * @throws {Error} When `skipDirectories` is not stated — see
112
121
  * {@link assertStatedScope}.
113
122
  */
114
- export function walkMarkdownTree(rootDir: string, { skipDirectories, addressContext }?: {
123
+ export function walkMarkdownTree(rootDir: string, { skipDirectories, addressContext, sources, renderDescription }?: {
115
124
  skipDirectories: readonly string[];
116
125
  addressContext?: object | undefined;
126
+ sources?: Map<string, {
127
+ raw: string;
128
+ }> | undefined;
129
+ renderDescription?: boolean | undefined;
117
130
  }): Generator<{
118
- file: string & NonSharedBuffer;
131
+ file: string;
119
132
  absPath: string;
120
133
  frontmatter: object | null;
121
134
  body: string;
@@ -364,11 +377,24 @@ export function defaultStats(): object;
364
377
  * omitted.
365
378
  * @param {readonly object[]} [opts.records] - The corpus, derived once per
366
379
  * compile and handed in. Required: see {@link assertSuppliedCorpus}.
380
+ * @param {Map<string, object[]>} [opts.sqlTables] - Prepared directives,
381
+ * including the anchors and Foundry page maps of their expanded bodies.
382
+ * @param {ReturnType<import("./metadata-index.mjs").loadForeignIndexes>} [opts.foreign] -
383
+ * Captured dependency address and reference views for this context.
367
384
  * @returns {{byShortcode: Map, types: Set}} From `buildWikilinkIndex`.
368
385
  */
369
- export function buildContentLinkIndex(contentBase: string, router?: object, { config, records }?: {
386
+ export function buildContentLinkIndex(contentBase: string, router?: object, { config, records, sqlTables, foreign: capturedForeign }?: {
370
387
  config?: object | undefined;
371
388
  records?: readonly object[] | undefined;
389
+ sqlTables?: Map<string, object[]> | undefined;
390
+ foreign?: {
391
+ index: Map<string, object>;
392
+ packages: Set<string>;
393
+ stale: Array<{
394
+ package: string;
395
+ reason: string;
396
+ }>;
397
+ } | undefined;
372
398
  }): {
373
399
  byShortcode: Map<any, any>;
374
400
  types: Set<any>;
@@ -50,6 +50,7 @@ export * as noteIds from "./note-ids.mjs";
50
50
  export * as metadataIndex from "./metadata-index.mjs";
51
51
  export * as foundryEntries from "./foundry-entries.mjs";
52
52
  export * as contentIndex from "./content-index.mjs";
53
+ export * as contentSession from "./content-session.mjs";
53
54
  export * as assetTypes from "./asset-types.mjs";
54
55
  export * as assetIndex from "./asset-index.mjs";
55
56
  export * as assetShipping from "./asset-shipping.mjs";
@@ -29,11 +29,12 @@
29
29
  * picture's address to the role its asset declares, so a figure page's
30
30
  * name reads `Map 1` rather than `Figure 1` where it is due — see
31
31
  * {@link module:engine/content-figures.scanFigures}.
32
+ * @param {import("./content-session.mjs").ContentSyntax} [syntax] - Session scanner projections.
32
33
  * @returns {Array<{name: string, anchorSlug: string|null, level: number,
33
34
  * classes: string[], figure: object|null, continues?: string,
34
35
  * markdown: string}>} Pages in document order.
35
36
  */
36
- export function splitPages(body: string, leadName?: string, resolveRole?: (address: string) => string | undefined): Array<{
37
+ export function splitPages(body: string, leadName?: string, resolveRole?: (address: string) => string | undefined, syntax?: import("./content-session.mjs").ContentSyntax): Array<{
37
38
  name: string;
38
39
  anchorSlug: string | null;
39
40
  level: number;
@@ -190,9 +191,10 @@ export function buildPages(rawPages: Array<object>, entryId: string, noteName: s
190
191
  * a picture's address to the role its asset declares, so a figure page's
191
192
  * name reads `Map 1` rather than `Figure 1` where it is due — see
192
193
  * {@link module:engine/content-figures.scanFigures}.
194
+ * @param {import("./content-session.mjs").ContentSyntax} [params.syntax] - Session scanner projections.
193
195
  * @returns {object} The JournalEntry document, keyed for the pack.
194
196
  */
195
- export function buildJournalEntry({ id, name, markdown, leadName, folder, stats, infoboxes, notice, resolveRole, }: {
197
+ export function buildJournalEntry({ id, name, markdown, leadName, folder, stats, infoboxes, notice, resolveRole, syntax, }: {
196
198
  id: string;
197
199
  name: string;
198
200
  markdown: string;
@@ -206,6 +208,7 @@ export function buildJournalEntry({ id, name, markdown, leadName, folder, stats,
206
208
  }[] | undefined;
207
209
  notice?: string | undefined;
208
210
  resolveRole?: ((address: string) => string | undefined) | undefined;
211
+ syntax?: import("./content-session.mjs").ContentSyntax | undefined;
209
212
  }): object;
210
213
  /**
211
214
  * Journals pack compiler.
@@ -319,6 +319,13 @@ export const NEAR_NAME_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[]
319
319
  * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
320
320
  */
321
321
  export const NAMING_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
322
+ /**
323
+ * The six present persons of the one optional regular verb pattern, in
324
+ * presentation order. Empty endings leave the citation form unchanged.
325
+ *
326
+ * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
327
+ */
328
+ export const VERB_PERSON_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
322
329
  /**
323
330
  * The `data` fields this module declares, in the order the vocabulary lists
324
331
  * them. {@link module:engine/language-data.checkLanguageData} checks what the