@heroiclands/package-build 23.9.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 (85) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +2 -0
  3. package/bin/content-commands.mjs +197 -68
  4. package/docs/api.md +336 -68
  5. package/docs/authoring/links-and-markup.md +114 -47
  6. package/docs/commands.md +92 -3
  7. package/docs/content-format.md +6 -1
  8. package/docs/reference/format-details.md +129 -14
  9. package/docs/reference/note-types.md +4 -4
  10. package/engine/anchors.mjs +8 -6
  11. package/engine/base-compiler.mjs +6 -4
  12. package/engine/changelog-group.mjs +58 -34
  13. package/engine/changelog-lint.mjs +6 -2
  14. package/engine/compendiums.mjs +15 -1
  15. package/engine/compile-corpus.mjs +14 -1
  16. package/engine/content-charset.mjs +13 -1
  17. package/engine/content-html.mjs +18 -4
  18. package/engine/content-icons.mjs +15 -1
  19. package/engine/content-images.mjs +28 -9
  20. package/engine/content-index.mjs +102 -23
  21. package/engine/content-links.mjs +27 -12
  22. package/engine/content-session.mjs +247 -0
  23. package/engine/content-sources.mjs +110 -0
  24. package/engine/content-spans.mjs +87 -8
  25. package/engine/content-tables.mjs +1 -0
  26. package/engine/content-tasklists.mjs +20 -4
  27. package/engine/diagnostics.mjs +17 -6
  28. package/engine/extension-attributes.mjs +28 -5
  29. package/engine/first-use.mjs +371 -0
  30. package/engine/foundry-entries.mjs +12 -9
  31. package/engine/generate.mjs +13 -4
  32. package/engine/helpers.mjs +43 -14
  33. package/engine/index-records.mjs +1 -0
  34. package/engine/index.mjs +3 -0
  35. package/engine/journals.mjs +9 -5
  36. package/engine/language-data.mjs +2 -2
  37. package/engine/language-rows.mjs +4 -4
  38. package/engine/language-terms.mjs +4 -4
  39. package/engine/lexical-sections.mjs +5 -5
  40. package/engine/markup-validation.mjs +7 -2
  41. package/engine/metadata-index.mjs +1 -0
  42. package/engine/name-markup.mjs +114 -0
  43. package/engine/name-rows.mjs +70 -0
  44. package/engine/names.mjs +460 -0
  45. package/engine/note-addresses.mjs +25 -4
  46. package/engine/path-charset.mjs +24 -1
  47. package/engine/pdf-render.mjs +15 -1
  48. package/engine/site-index.mjs +5 -1
  49. package/engine/sql-tables.mjs +76 -15
  50. package/engine/stub-lint.mjs +4 -3
  51. package/engine/yaml-lint.mjs +3 -5
  52. package/hugo-theme/static/css/style.css +4 -0
  53. package/package.json +1 -1
  54. package/types/engine/anchors.d.mts +4 -2
  55. package/types/engine/compendiums.d.mts +5 -1
  56. package/types/engine/compile-corpus.d.mts +5 -2
  57. package/types/engine/content-charset.d.mts +5 -1
  58. package/types/engine/content-html.d.mts +5 -1
  59. package/types/engine/content-icons.d.mts +5 -1
  60. package/types/engine/content-images.d.mts +5 -1
  61. package/types/engine/content-index.d.mts +39 -19
  62. package/types/engine/content-links.d.mts +22 -8
  63. package/types/engine/content-session.d.mts +108 -0
  64. package/types/engine/content-sources.d.mts +32 -0
  65. package/types/engine/content-spans.d.mts +3 -5
  66. package/types/engine/content-tasklists.d.mts +5 -1
  67. package/types/engine/diagnostics.d.mts +10 -0
  68. package/types/engine/extension-attributes.d.mts +16 -2
  69. package/types/engine/first-use.d.mts +31 -0
  70. package/types/engine/foundry-entries.d.mts +11 -4
  71. package/types/engine/generate.d.mts +5 -1
  72. package/types/engine/helpers.d.mts +30 -7
  73. package/types/engine/index.d.mts +1 -0
  74. package/types/engine/journals.d.mts +5 -2
  75. package/types/engine/lexical-sections.d.mts +3 -2
  76. package/types/engine/markup-validation.d.mts +2 -1
  77. package/types/engine/name-markup.d.mts +176 -0
  78. package/types/engine/name-rows.d.mts +35 -0
  79. package/types/engine/names.d.mts +356 -0
  80. package/types/engine/note-addresses.d.mts +3 -3
  81. package/types/engine/path-charset.d.mts +16 -1
  82. package/types/engine/site-build.d.mts +2 -2
  83. package/types/engine/site-index.d.mts +3 -1
  84. package/types/engine/sql-tables.d.mts +7 -2
  85. package/types/engine/stub-lint.d.mts +3 -1
@@ -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
  };
@@ -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;
@@ -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
  *
@@ -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
+ }>;
@@ -13,9 +13,10 @@
13
13
  * @param {string} entryId - The entry's id, which page ids hash against.
14
14
  * @param {string} body - The note's markdown body.
15
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.
16
17
  * @returns {Record<string, string>} The anchors.
17
18
  */
18
- 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>;
19
20
  /**
20
21
  * The manifest entries a single note produces.
21
22
  *
@@ -37,11 +38,17 @@ export function anchorsOf(entryUuid: string, entryId: string, body: string, name
37
38
  * documentation journal's is {@link itemDocEntryId} of that. Stating it here is
38
39
  * what lets the content index publish an identity it did not re-derive.
39
40
  *
40
- * @param {object} ctx - Resolved identities: `{ contentPackage,
41
- * 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.
42
42
  * @returns {Array<object>} One or two entries, in {@link buildManifest}'s shape.
43
43
  */
44
- 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>;
45
52
  /**
46
53
  * Every note this package publishes, as manifest entries.
47
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;
@@ -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;
@@ -366,12 +379,22 @@ export function defaultStats(): object;
366
379
  * compile and handed in. Required: see {@link assertSuppliedCorpus}.
367
380
  * @param {Map<string, object[]>} [opts.sqlTables] - Prepared directives,
368
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.
369
384
  * @returns {{byShortcode: Map, types: Set}} From `buildWikilinkIndex`.
370
385
  */
371
- export function buildContentLinkIndex(contentBase: string, router?: object, { config, records, sqlTables }?: {
386
+ export function buildContentLinkIndex(contentBase: string, router?: object, { config, records, sqlTables, foreign: capturedForeign }?: {
372
387
  config?: object | undefined;
373
388
  records?: readonly object[] | undefined;
374
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;
375
398
  }): {
376
399
  byShortcode: Map<any, any>;
377
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.
@@ -35,15 +35,16 @@ export function lexicalSectionsFor(records: object[], all?: object[]): Map<strin
35
35
  *
36
36
  * @param {string} markdown - The body with authored directives expanded.
37
37
  * @param {LexicalSection[]} [sections=[]] - From {@link lexicalSectionsFor}.
38
- * @param {{authored?: string, lineMap?: object[], generatedFrom?: number}} [context] -
38
+ * @param {{authored?: string, lineMap?: object[], generatedFrom?: number, syntax?: import("./content-session.mjs").ContentSyntax}} [context] -
39
39
  * The original authored body, source mappings and the start of appended
40
40
  * nonlexical generated sections. Without `authored`, `markdown` is the source.
41
41
  * @returns {{markdown: string, lineMap: object[], generatedFrom?: number}}
42
42
  */
43
- export function insertLexicalSections(markdown: string, sections?: LexicalSection[], { authored, lineMap, generatedFrom }?: {
43
+ export function insertLexicalSections(markdown: string, sections?: LexicalSection[], { authored, lineMap, generatedFrom, syntax }?: {
44
44
  authored?: string;
45
45
  lineMap?: object[];
46
46
  generatedFrom?: number;
47
+ syntax?: import("./content-session.mjs").ContentSyntax;
47
48
  }): {
48
49
  markdown: string;
49
50
  lineMap: object[];
@@ -3,9 +3,10 @@
3
3
  * Frontmatter is masked so literal YAML values cannot become body markup.
4
4
  * @param {string} source - Complete Markdown source, optionally with frontmatter.
5
5
  * @param {string} file - Diagnostic filename.
6
+ * @param {import("./content-session.mjs").ContentSyntax} [syntax] - Exact-text scanner projections.
6
7
  * @returns {Array<{file: string, line: number, column: number, severity: string, message: string}>} Syntax errors.
7
8
  */
8
- export function markupFindings(source: string, file: string): Array<{
9
+ export function markupFindings(source: string, file: string, syntax?: import("./content-session.mjs").ContentSyntax): Array<{
9
10
  file: string;
10
11
  line: number;
11
12
  column: number;
@@ -0,0 +1,176 @@
1
+ /** Pure projections of parsed inline text and original marked-name positions. */
2
+ /**
3
+ * An original source range with its opening file position.
4
+ * @typedef {object} SpanSourceSite
5
+ * @property {number} start - Inclusive UTF-16 source offset.
6
+ * @property {number} end - Exclusive UTF-16 source offset.
7
+ * @property {number} line - Original 1-based source line.
8
+ * @property {number} column - Original 1-based UTF-16 source column.
9
+ */
10
+ /**
11
+ * The retained attribute key and value at their authored positions.
12
+ * @typedef {object} SpanAttributePosition
13
+ * @property {SpanSourceSite} key - Attribute name, without surrounding whitespace.
14
+ * @property {SpanSourceSite} value - Authored value, without its surrounding quotes.
15
+ */
16
+ /**
17
+ * One actual bracketed span and its renderer-derived visible text.
18
+ * @typedef {object} ContentSpan
19
+ * @property {string} id - Normalized anchor, or an empty string.
20
+ * @property {number} start - Inclusive UTF-16 offset of the opening bracket.
21
+ * @property {number} end - Exclusive UTF-16 offset after the complete span.
22
+ * @property {number} line - Original 1-based line of the opening bracket.
23
+ * @property {number} column - Original 1-based UTF-16 column of the bracket.
24
+ * @property {string} text - Inline label source passed to the existing parser.
25
+ * @property {number} contentEnd - Original UTF-16 offset of the closing label bracket.
26
+ * @property {string} visible - Visible text from the actual parsed children.
27
+ * @property {ReadonlyArray<readonly [string, string]>} attrs - Retained permitted HTML attributes.
28
+ * @property {Record<string, SpanAttributePosition>} attributePositions - Authored key/value sites by retained attribute name.
29
+ */
30
+ /**
31
+ * One located span-grammar or refused-attribute finding.
32
+ * @typedef {object} SpanFinding
33
+ * @property {number} line - Original 1-based source line.
34
+ * @property {number} column - Original 1-based UTF-16 source column.
35
+ * @property {string} message - The existing grammar or attribute diagnostic.
36
+ */
37
+ /**
38
+ * A captured authored span inventory.
39
+ * @typedef {object} SpanScan
40
+ * @property {ContentSpan[]} spans - All actual parsed bracketed spans in source order.
41
+ * @property {ContentSpan[]} names - The same span objects carrying the exact name class.
42
+ * @property {SpanFinding[]} errors - Located diagnostics in source order.
43
+ */
44
+ /**
45
+ * Visible name text from the renderer's actual parsed children. Footnote
46
+ * markers and images supply no name text; references have already resolved.
47
+ * @param {readonly object[]} tokens - Parsed inline tokens.
48
+ * @returns {string} Text without inline formatting or annotation markers.
49
+ */
50
+ export function inlineText(tokens: readonly object[]): string;
51
+ /**
52
+ * Rebase an authored body's captured names to its original raw file.
53
+ * The inventory is read only; no source parsing or second marker scan occurs.
54
+ * @template {ContentSpan} Span
55
+ * @param {readonly Span[]} spans - The captured body's name spans.
56
+ * @param {{raw: string, bodyLine?: number, bodyColumn?: number}} source - Original body origin.
57
+ * @returns {Span[]} Copies retaining the span metadata with raw offsets and file coordinates.
58
+ */
59
+ export function rebaseNameSpans<Span extends ContentSpan>(spans: readonly Span[], { raw, bodyLine, bodyColumn }: {
60
+ raw: string;
61
+ bodyLine?: number;
62
+ bodyColumn?: number;
63
+ }): Span[];
64
+ /**
65
+ * An original source range with its opening file position.
66
+ */
67
+ export type SpanSourceSite = {
68
+ /**
69
+ * - Inclusive UTF-16 source offset.
70
+ */
71
+ start: number;
72
+ /**
73
+ * - Exclusive UTF-16 source offset.
74
+ */
75
+ end: number;
76
+ /**
77
+ * - Original 1-based source line.
78
+ */
79
+ line: number;
80
+ /**
81
+ * - Original 1-based UTF-16 source column.
82
+ */
83
+ column: number;
84
+ };
85
+ /**
86
+ * The retained attribute key and value at their authored positions.
87
+ */
88
+ export type SpanAttributePosition = {
89
+ /**
90
+ * - Attribute name, without surrounding whitespace.
91
+ */
92
+ key: SpanSourceSite;
93
+ /**
94
+ * - Authored value, without its surrounding quotes.
95
+ */
96
+ value: SpanSourceSite;
97
+ };
98
+ /**
99
+ * One actual bracketed span and its renderer-derived visible text.
100
+ */
101
+ export type ContentSpan = {
102
+ /**
103
+ * - Normalized anchor, or an empty string.
104
+ */
105
+ id: string;
106
+ /**
107
+ * - Inclusive UTF-16 offset of the opening bracket.
108
+ */
109
+ start: number;
110
+ /**
111
+ * - Exclusive UTF-16 offset after the complete span.
112
+ */
113
+ end: number;
114
+ /**
115
+ * - Original 1-based line of the opening bracket.
116
+ */
117
+ line: number;
118
+ /**
119
+ * - Original 1-based UTF-16 column of the bracket.
120
+ */
121
+ column: number;
122
+ /**
123
+ * - Inline label source passed to the existing parser.
124
+ */
125
+ text: string;
126
+ /**
127
+ * - Original UTF-16 offset of the closing label bracket.
128
+ */
129
+ contentEnd: number;
130
+ /**
131
+ * - Visible text from the actual parsed children.
132
+ */
133
+ visible: string;
134
+ /**
135
+ * - Retained permitted HTML attributes.
136
+ */
137
+ attrs: ReadonlyArray<readonly [string, string]>;
138
+ /**
139
+ * - Authored key/value sites by retained attribute name.
140
+ */
141
+ attributePositions: Record<string, SpanAttributePosition>;
142
+ };
143
+ /**
144
+ * One located span-grammar or refused-attribute finding.
145
+ */
146
+ export type SpanFinding = {
147
+ /**
148
+ * - Original 1-based source line.
149
+ */
150
+ line: number;
151
+ /**
152
+ * - Original 1-based UTF-16 source column.
153
+ */
154
+ column: number;
155
+ /**
156
+ * - The existing grammar or attribute diagnostic.
157
+ */
158
+ message: string;
159
+ };
160
+ /**
161
+ * A captured authored span inventory.
162
+ */
163
+ export type SpanScan = {
164
+ /**
165
+ * - All actual parsed bracketed spans in source order.
166
+ */
167
+ spans: ContentSpan[];
168
+ /**
169
+ * - The same span objects carrying the exact name class.
170
+ */
171
+ names: ContentSpan[];
172
+ /**
173
+ * - Located diagnostics in source order.
174
+ */
175
+ errors: SpanFinding[];
176
+ };