@heroiclands/package-build 23.10.0 → 23.12.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 (74) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +1 -0
  3. package/assets/preview/check-sanitizer.go +68 -0
  4. package/assets/preview/gitea-theme.js +36 -0
  5. package/assets/preview/gitea.ini +127 -0
  6. package/assets/styles/content-base.css +119 -16
  7. package/bin/content-commands.mjs +2 -2
  8. package/bin/package-build-preview.mjs +75 -0
  9. package/content-config.mjs +11 -0
  10. package/docs/api.md +131 -45
  11. package/docs/authoring/first-note.md +10 -0
  12. package/docs/authoring/frontmatter.md +1 -1
  13. package/docs/authoring/links-and-markup.md +111 -6
  14. package/docs/commands.md +46 -2
  15. package/docs/configuration.md +21 -4
  16. package/docs/content-format.md +4 -2
  17. package/docs/design.md +19 -0
  18. package/docs/guides/book.md +11 -0
  19. package/docs/guides/site.md +7 -0
  20. package/docs/reference/format-details.md +82 -27
  21. package/docs/reference/note-types.md +0 -1
  22. package/docs/reference/theme-pages.md +1 -1
  23. package/docs/source-preview.md +172 -0
  24. package/engine/anchors.mjs +36 -4
  25. package/engine/base-compiler.mjs +7 -8
  26. package/engine/being-age.mjs +19 -105
  27. package/engine/calendar-notes.mjs +3 -25
  28. package/engine/compile-corpus.mjs +2 -1
  29. package/engine/content-format.yaml +0 -1
  30. package/engine/content-index.mjs +9 -9
  31. package/engine/content-links.mjs +41 -18
  32. package/engine/content-spans.mjs +54 -7
  33. package/engine/current-date.mjs +37 -0
  34. package/engine/definition-list.mjs +58 -0
  35. package/engine/frontmatter-lint.mjs +7 -2
  36. package/engine/helpers.mjs +6 -2
  37. package/engine/index.mjs +2 -0
  38. package/engine/infobox.mjs +1 -5
  39. package/engine/note-vocabulary.mjs +2 -2
  40. package/engine/npc-generate.mjs +7 -6
  41. package/engine/pdf-render.mjs +36 -3
  42. package/engine/preview-current-date.mjs +60 -0
  43. package/engine/site-build.mjs +5 -5
  44. package/engine/site-index.mjs +28 -6
  45. package/engine/site-preview.mjs +641 -325
  46. package/engine/web-wikilinks.mjs +33 -12
  47. package/engine/wikilink-syntax.mjs +65 -8
  48. package/engine/wikilinks.mjs +29 -11
  49. package/hugo-theme/layouts/_default/list.html +1 -1
  50. package/hugo-theme/layouts/blog/list.html +1 -1
  51. package/hugo-theme/layouts/blog-post/single.html +1 -1
  52. package/hugo-theme/layouts/partials/dictionary-lists.html +53 -0
  53. package/hugo-theme/layouts/partials/footer.html +2 -0
  54. package/hugo-theme/layouts/partials/page-body.html +1 -1
  55. package/hugo-theme/static/css/style.css +39 -9
  56. package/hugo-theme/static/js/inline-spoilers.js +23 -0
  57. package/package.json +8 -2
  58. package/types/content-config.d.mts +8 -0
  59. package/types/engine/anchors.d.mts +2 -1
  60. package/types/engine/base-compiler.d.mts +1 -1
  61. package/types/engine/being-age.d.mts +10 -44
  62. package/types/engine/calendar-notes.d.mts +2 -6
  63. package/types/engine/compile-corpus.d.mts +2 -1
  64. package/types/engine/content-index.d.mts +3 -1
  65. package/types/engine/content-spans.d.mts +7 -1
  66. package/types/engine/current-date.d.mts +9 -0
  67. package/types/engine/definition-list.d.mts +8 -0
  68. package/types/engine/frontmatter-lint.d.mts +6 -2
  69. package/types/engine/index.d.mts +2 -0
  70. package/types/engine/npc-generate.d.mts +2 -2
  71. package/types/engine/preview-current-date.d.mts +8 -0
  72. package/types/engine/site-index.d.mts +5 -1
  73. package/types/engine/site-preview.d.mts +40 -33
  74. package/types/engine/wikilink-syntax.d.mts +40 -3
@@ -15,11 +15,9 @@ export function parseAgeMagnitude(raw: unknown): {
15
15
  *
16
16
  * @param {unknown} bornRaw - The being's authored `born`, or `undefined`/`null`
17
17
  * where it wrote none.
18
- * @param {unknown} presentRaw - The package's `data.present`, as
19
- * {@link module:engine/calendar-notes.worldInvariants} read it, or
20
- * `undefined`/`null` where the package states none.
18
+ * @param {unknown} presentRaw - The configured neutral current date.
21
19
  * @returns {number|null} The age, or `null` where `born` is `unknown` or
22
- * absent, where no present is declared, or where either date fails to parse.
20
+ * absent or future, or where either date fails to parse.
23
21
  */
24
22
  export function computeAge(bornRaw: unknown, presentRaw: unknown, dateContext?: {}): number | null;
25
23
  /**
@@ -35,58 +33,26 @@ export function computeAge(bornRaw: unknown, presentRaw: unknown, dateContext?:
35
33
  * itself untouched.
36
34
  *
37
35
  * @param {object} fm - A note's frontmatter, as freshly parsed.
38
- * @param {unknown} presentRaw - The package's declared present, or
39
- * `undefined`/`null` where it states none.
36
+ * @param {unknown} presentRaw - The configured neutral current date.
40
37
  * @returns {void}
41
38
  */
42
39
  export function applyComputedBeingAge(fm: object, presentRaw: unknown, dateContext?: {}): void;
43
40
  /**
44
- * A being's `data.present`, read from every `place` note a batch of records or
45
- * frontmatter objects carries.
46
- *
47
- * Two shapes reach this by way of two thin adapters, because the callers that
48
- * need a present hold their notes two different ways: a flat index record
49
- * (`type` and `data` at the top level) and a raw walk's `{frontmatter}` pair.
50
- * Neither shape is the `{fm, type}` pair {@link worldInvariants} reads, so each
51
- * adapter states the one conversion its caller needs rather than asking every
52
- * caller to know it.
53
- *
54
- * @param {readonly object[]|undefined} records - Flat index records — a
55
- * content-index record, or anything else spreading a note's frontmatter to
56
- * its own top level.
57
- * @returns {unknown} The present, or `null` where no `place` note in the batch
58
- * declares one.
59
- */
60
- export function presentAmongRecords(records: readonly object[] | undefined): unknown;
61
- /**
62
- * The present read from a batch of raw frontmatter objects.
63
- *
64
- * @param {readonly (object|null|undefined)[]|undefined} frontmatters - One
65
- * entry per note walked, as parsed — never a record.
66
- * @returns {unknown} The present, or `null` where none is declared.
67
- */
68
- export function presentAmongFrontmatters(frontmatters: readonly (object | null | undefined)[] | undefined): unknown;
69
- /**
70
- * Check a being's authored `age` against what `born` and the package's present
71
- * compute.
41
+ * Check an authored age against the birth date and configured current date.
72
42
  *
73
43
  * Registered as `being`'s whole-note `check` in `note-vocabulary.mjs`, run the
74
44
  * way `checkPlace` and `checkCalendarNote` are — beside every other check the
75
45
  * closed `data:` container earns, with the same `index`.
76
46
  *
77
- * Two silences, both deliberate: beside `born: unknown` or an absent `born`
78
- * there is nothing to disagree with, and a package that declares no present
79
- * computes no age to disagree with either — both cases are read by
80
- * {@link computeAge} returning `null`, and neither says a word.
47
+ * An unknown or absent birth date supplies no computed age to compare.
81
48
  *
82
- * @param {object} note - The note, as the link index hands it over —
83
- * {@link module:engine/calendar-notes.worldInvariants}'s `index.notes` shape.
49
+ * @param {object} note - The note, as the link index hands it over.
84
50
  * @param {object} [opts]
85
- * @param {object} [opts.index] - The link index, holding the package's `place`
86
- * notes. Its absence skips the check, the way a reference check with no
87
- * index skips reporting every reference as dead.
51
+ * @param {object} [opts.index] - Calendar context for authored birth dates.
52
+ * @param {string} [opts.currentDate="720.1"] - The configured neutral current date.
88
53
  * @returns {object[]} Findings — at most one `warning`, naming both values.
89
54
  */
90
- export function checkBeingAge(note: object, { index }?: {
55
+ export function checkBeingAge(note: object, { index, currentDate }?: {
91
56
  index?: object | undefined;
57
+ currentDate?: string | undefined;
92
58
  }): object[];
@@ -5,13 +5,12 @@
5
5
  * note has no year, and every question that depends on one goes unasked.
6
6
  *
7
7
  * @param {object} [index] - The link index, holding every note in the package.
8
- * @returns {{year: object|null, present: unknown, moon: object|null,
8
+ * @returns {{year: object|null, moon: object|null,
9
9
  * body: object|null, moonName: string|null, yearNote: object|null,
10
10
  * moonNote: object|null}} What the package's notes state.
11
11
  */
12
12
  export function worldInvariants(index?: object): {
13
13
  year: object | null;
14
- present: unknown;
15
14
  moon: object | null;
16
15
  body: object | null;
17
16
  moonName: string | null;
@@ -41,9 +40,6 @@ export function checkCalendarDateFormats(note: any): {
41
40
  * - a moon with no year is refused, because a cycle measured in days has
42
41
  * nothing to be measured against.
43
42
  *
44
- * `data.present` is read through the parser a note's dates go through, with the
45
- * corpus in hand, so a present written in an era resolves where it is written.
46
- *
47
43
  * @param {object} note - The note, as the link index hands it over.
48
44
  * @param {object} [opts]
49
45
  * @param {object} [opts.index] - The link index, holding every note.
@@ -185,7 +181,7 @@ export const CALENDAR_FIELDS: readonly object[];
185
181
  * The `data:` keys a world or a celestial body may write.
186
182
  *
187
183
  * Four groups, and which note carries which is a fact about the body rather
188
- * than a convention: a planet states its own size, tilt, year and present; a
184
+ * than a convention: a planet states its own size, tilt and year; a
189
185
  * moon states its own size, orbit and cycle.
190
186
  *
191
187
  * @type {readonly object[]}
@@ -16,7 +16,7 @@
16
16
  * the index refuses is a note the compile must still *report*, exactly as the
17
17
  * compile loop reported it when the loop was the first to see it.
18
18
  * @param {import("./content-session.mjs").ContentSession} [opts.session] - Captured context shared with other operations.
19
- * @returns {Promise<{records: object[], parsed: Map<string, object>, linkIndex: object, sqlTables: Map<string, object[]>|undefined, problems: object[], syntax?: import("./content-session.mjs").ContentSyntax}>} The corpus,
19
+ * @returns {Promise<{currentDate: string, records: object[], parsed: Map<string, object>, linkIndex: object, sqlTables: Map<string, object[]>|undefined, problems: object[], syntax?: import("./content-session.mjs").ContentSyntax}>} The corpus,
20
20
  * its indexes, and the notes it could not record.
21
21
  */
22
22
  export function buildCompileCorpus({ contentBase, assetsBase, skipDirectories, router, config, problems, session, }: {
@@ -28,6 +28,7 @@ export function buildCompileCorpus({ contentBase, assetsBase, skipDirectories, r
28
28
  problems?: object[] | undefined;
29
29
  session?: import("./content-session.mjs").ContentSession | undefined;
30
30
  }): Promise<{
31
+ currentDate: string;
31
32
  records: object[];
32
33
  parsed: Map<string, object>;
33
34
  linkIndex: object;
@@ -234,6 +234,7 @@ export function resolveCultures(records: Array<Record<string, any>>, { contentPa
234
234
  * asserts that it does not exist. The same holds for a culture conflict: a
235
235
  * reader receives each as a finding, and the emitter throws once, listing
236
236
  * every one.
237
+ * @param {string} [options.currentDate="720.1"] - Setting-wide neutral current date.
237
238
  * @param {(key: string) => ({culture?: unknown}|undefined)} [options.foreignRecord] -
238
239
  * Another package's entry for a canonical address, read for a parent that
239
240
  * package publishes; see {@link resolveCultures}.
@@ -246,13 +247,14 @@ export function resolveCultures(records: Array<Record<string, any>>, { contentPa
246
247
  * @returns {Array<Record<string, any>>} The records, in a total order that does
247
248
  * not depend on directory-read order.
248
249
  */
249
- export function collectContentIndex(contentBase: string, { contentPackage, skipDirectories, assetsBase, manifest, problems, addressContext, foreignRecord, parsed, sources, syntax, nameTypes, }: {
250
+ export function collectContentIndex(contentBase: string, { contentPackage, currentDate, skipDirectories, assetsBase, manifest, problems, addressContext, foreignRecord, parsed, sources, syntax, nameTypes, }: {
250
251
  contentPackage: string;
251
252
  skipDirectories: readonly string[];
252
253
  assetsBase?: string | undefined;
253
254
  manifest?: object | undefined;
254
255
  addressContext?: object | undefined;
255
256
  problems?: object[] | undefined;
257
+ currentDate?: string | undefined;
256
258
  foreignRecord?: ((key: string) => ({
257
259
  culture?: unknown;
258
260
  } | undefined)) | undefined;
@@ -1,7 +1,13 @@
1
1
  /** Add bracket spans after recognized Markdown links and footnotes.
2
2
  * @param {import("markdown-it")} md - Markdown parser to extend.
3
+ * @param {object} [options] - Surface presentation.
4
+ * @param {boolean} [options.source] - Emit opaque authored source tokens for buffer preview.
5
+ * @param {boolean} [options.spoilers] - Add focusable CSS spoiler wrappers (Foundry).
3
6
  */
4
- export function spanMarkdownPlugin(md: typeof import("markdown-it")): void;
7
+ export function spanMarkdownPlugin(md: typeof import("markdown-it"), { spoilers, source }?: {
8
+ source?: boolean | undefined;
9
+ spoilers?: boolean | undefined;
10
+ }): void;
5
11
  /** Read span anchors and located attribute diagnostics outside literal code.
6
12
  * @param {string} source - Authored Markdown.
7
13
  * @returns {import("./name-markup.mjs").SpanScan} Actual spans, explicit names and located diagnostics.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Validate a quoted neutral date and preserve its authored day digits.
3
+ * @param {unknown} [value] - Explicit configuration or adapter override.
4
+ * @returns {string} Validated neutral date; undefined selects the default.
5
+ * @throws {TypeError} An explicit value is not a valid neutral date string.
6
+ */
7
+ export function normalizeCurrentDate(value?: unknown): string;
8
+ /** Default day on the neutral setting timeline. */
9
+ export const DEFAULT_CURRENT_DATE: "720.1";
@@ -0,0 +1,8 @@
1
+ /** Semantic dictionary rows for HTML definition lists. @module */
2
+ /**
3
+ * Group each term and its definitions without reparsing their Markdown tokens.
4
+ * Register after markdown-it-deflist; nested blocks and footnotes retain their
5
+ * document context.
6
+ * @param {import("markdown-it")} md - An HTML Markdown parser with definition lists.
7
+ */
8
+ export function dictionaryListPlugin(md: typeof import("markdown-it")): void;
@@ -32,6 +32,7 @@ export function systemAddressFindings(note: object, { index, schemas, systems }?
32
32
  * @param {object} note - A note from the link index (`{fm, file, raw, type}`).
33
33
  * @param {object} opts
34
34
  * @param {Record<string, readonly object[]>} opts.schemas - Type → declaration.
35
+ * @param {string} [opts.currentDate="720.1"] - Setting-wide neutral date for age checks.
35
36
  * @param {object} [opts.index] - The link index, for the reference check, which
36
37
  * runs through its `shortcodeHit`. Its absence skips that check rather than
37
38
  * reporting every reference as dead.
@@ -59,8 +60,9 @@ export function systemAddressFindings(note: object, { index, schemas, systems }?
59
60
  * about those keys.
60
61
  * @returns {object[]} Findings, each with a locator where one is obtainable.
61
62
  */
62
- export function lintNote(note: object, { schemas, index, vocabulary, packs, emittedArt, addressContext, systems, }: {
63
+ export function lintNote(note: object, { schemas, index, currentDate, vocabulary, packs, emittedArt, addressContext, systems, }: {
63
64
  schemas: Record<string, readonly object[]>;
65
+ currentDate?: string | undefined;
64
66
  index?: object | undefined;
65
67
  vocabulary?: Record<string, object> | undefined;
66
68
  emittedArt?: ((type: string) => {
@@ -77,6 +79,7 @@ export function lintNote(note: object, { schemas, index, vocabulary, packs, emit
77
79
  * @param {object} index - From `buildLinkIndex`.
78
80
  * @param {object} opts
79
81
  * @param {Record<string, readonly object[]>} opts.schemas - Type → declaration.
82
+ * @param {string} [opts.currentDate="720.1"] - Setting-wide neutral date for age checks.
80
83
  * @param {Record<string, object>} [opts.vocabulary] - Type → the closed regions
81
84
  * it declares; see {@link lintNote}.
82
85
  * @param {boolean} [opts.references=true] - Whether to check references.
@@ -90,8 +93,9 @@ export function lintNote(note: object, { schemas, index, vocabulary, packs, emit
90
93
  * @returns {{findings: object[], notes: number}} The findings, and how many
91
94
  * notes were inspected.
92
95
  */
93
- export function lintFrontmatter(index: object, { schemas, vocabulary, packs, emittedArt, references, systems }: {
96
+ export function lintFrontmatter(index: object, { schemas, vocabulary, packs, emittedArt, references, systems, currentDate }: {
94
97
  schemas: Record<string, readonly object[]>;
98
+ currentDate?: string | undefined;
95
99
  vocabulary?: Record<string, object> | undefined;
96
100
  references?: boolean | undefined;
97
101
  systems?: Readonly<Record<string, SystemBlockSpec>> | undefined;
@@ -39,6 +39,8 @@ export * as cultureChoice from "./culture-choice.mjs";
39
39
  export * as reckoningMarkers from "./reckoning-markers.mjs";
40
40
  export * as calendarNotes from "./calendar-notes.mjs";
41
41
  export * as calendarArtifacts from "./calendar-artifacts.mjs";
42
+ export * as definitionList from "./definition-list.mjs";
43
+ export * as currentDate from "./current-date.mjs";
42
44
  export * as beingAge from "./being-age.mjs";
43
45
  export * as contentSlug from "./content-slug.mjs";
44
46
  export * as contentAddress from "./content-address.mjs";
@@ -59,7 +59,7 @@ export function appearanceParagraph({ name, age, gender, height, frame, appearan
59
59
  * about a name that does not fit.
60
60
  * @param {string} [context.seed] - Text joined to the spec's own seed, to draw a
61
61
  * different person from the same spec.
62
- * @param {unknown} [context.present] - The package's present, or `null`.
62
+ * @param {unknown} [context.currentDate] - The setting's neutral current date, defaulting to "720.1".
63
63
  * @param {object} [context.dateContext] - The reckoning context the present
64
64
  * and `born` are read in, with `daysPerYear`.
65
65
  * @param {{icon?: unknown, species?: unknown, packFolder?: unknown}} [context.defaults] -
@@ -75,7 +75,7 @@ export function generateNpc(spec: object, context: {
75
75
  names: object;
76
76
  language?: string | undefined;
77
77
  seed?: string | undefined;
78
- present?: unknown;
78
+ currentDate?: unknown;
79
79
  dateContext?: object | undefined;
80
80
  defaults?: {
81
81
  icon?: unknown;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Read only currentDate from the nearest package configuration.
3
+ * YAML does not load the full build configuration. An MJS configuration executes
4
+ * its own module, as any caller opting into a code configuration expects.
5
+ * @param {string} root - Directory at which configuration discovery starts.
6
+ * @returns {Promise<string>} Validated neutral current date.
7
+ */
8
+ export function readPreviewCurrentDate(root: string): Promise<string>;
@@ -4,6 +4,8 @@
4
4
  * @param {readonly SiteEntry[]} entries - Every page the site will publish,
5
5
  * each already knowing its own `url`.
6
6
  * @param {object} [options] - Cross-package inputs.
7
+ * @param {string} [options.package] - The builder's own content package.
8
+ * @param {string} [options.currentDate="720.1"] - Setting-wide neutral date for recurring events.
7
9
  * @param {Map<string, {package: string, type?: string}>} [options.foreignIndex]
8
10
  * The merged index from `loadForeignIndexes`. Omit when the build publishes
9
11
  * no cross-package links.
@@ -16,7 +18,9 @@
16
18
  * @param {import("./content-session.mjs").ContentSyntax} [options.syntax] - Exact-text scanner projections.
17
19
  * @returns {SiteIndex} The index, and what could not be addressed unambiguously.
18
20
  */
19
- export function buildSiteIndex(entries: readonly SiteEntry[], { foreignIndex, noIndexPackages, package: builderPackage, records, foreignReferences, syntax, }?: {
21
+ export function buildSiteIndex(entries: readonly SiteEntry[], { foreignIndex, noIndexPackages, package: builderPackage, currentDate, records, foreignReferences, syntax, }?: {
22
+ package?: string | undefined;
23
+ currentDate?: string | undefined;
20
24
  foreignIndex?: Map<string, {
21
25
  package: string;
22
26
  type?: string;
@@ -1,34 +1,41 @@
1
- /** Prepare saved project state once, then render live text without writing a site. */
2
- export function prepareSitePreview({ config }?: {
3
- config?: import("../content-config.mjs").ContentBuildConfig | undefined;
4
- }): Promise<{
5
- /** Render the active buffer. Other local and foreign notes remain saved. */
6
- render(file: any, text: any): Promise<{
7
- ok: boolean;
8
- findings: {
9
- file: string;
10
- severity: string;
11
- message: string;
12
- }[];
13
- markdown?: undefined;
14
- frontmatter?: undefined;
15
- } | {
16
- ok: boolean;
17
- findings: {
18
- path: string;
19
- link: string;
20
- file: string;
21
- severity: string;
22
- }[];
23
- markdown?: undefined;
24
- frontmatter?: undefined;
25
- } | {
26
- ok: boolean;
27
- markdown: string;
28
- frontmatter: any;
29
- findings: never[];
30
- }>;
31
- /** Re-read saved notes and dependency indexes after a save. */
32
- refresh(): Promise<void>;
33
- close(): Promise<void>;
1
+ /**
2
+ * Render one complete buffer with no configuration, index, filesystem or database.
3
+ * @param {string} text - Markdown, optionally beginning with YAML frontmatter.
4
+ * @param {{dark?: boolean, infobox?: boolean, file?: string, currentDate?: string}} [options] - Explicit palette, profile visibility, diagnostic filename and neutral current date (default "720.1").
5
+ * @returns {{ok: boolean, html: string, frontmatter: object, findings: object[]}} HTML and metadata with locally calculable ages; unavailable ages remain absent or null.
6
+ */
7
+ export function renderPreview(text: string, { dark, infobox, file, currentDate }?: {
8
+ dark?: boolean;
9
+ infobox?: boolean;
10
+ file?: string;
11
+ currentDate?: string;
12
+ }): {
13
+ ok: boolean;
14
+ html: string;
15
+ frontmatter: object;
16
+ findings: object[];
17
+ };
18
+ /**
19
+ * Opt in to reading only the project's currentDate setting; never called by renderPreview.
20
+ * @param {string} root - Directory from which to find the nearest package configuration.
21
+ * @returns {Promise<string>} Validated neutral date, or the shared default when no setting is present.
22
+ */
23
+ export function readPreviewCurrentDate(root: string): Promise<string>;
24
+ /** Both explicit palettes, scoped to the preview container. */
25
+ export function previewStyles(): string;
26
+ /** Explicit dependency policy: a new registered helper requires a preview decision. */
27
+ export const PREVIEW_HELPERS: Readonly<{
28
+ eq: "local";
29
+ gt: "local";
30
+ gte: "local";
31
+ lt: "local";
32
+ lte: "local";
33
+ not: "local";
34
+ and: "local";
35
+ or: "local";
36
+ words: "local";
37
+ digits: "local";
38
+ sql: "dependent";
39
+ dateformat: "dependent";
40
+ ref: "reference";
34
41
  }>;
@@ -9,6 +9,7 @@
9
9
  * @property {string} anchor - The `#section` slug, `""` when there is none.
10
10
  * @property {string|null} display - The text after `|`, or `null` when the link
11
11
  * is unlabelled. `null` and `""` differ: an author may write `[[x|]]`.
12
+ * @property {ReturnType<typeof parseExtensionAttributes>} [attributes] - Trailing extension attributes.
12
13
  * @property {boolean} labelled - Whether a `|` was present at all. A link
13
14
  * without one addresses nothing and is a finding — see
14
15
  * {@link unlabelledLinkMessage} — so every reader has to be able to ask.
@@ -20,9 +21,10 @@
20
21
  * not the whole link, so a caller that has already matched does not re-match.
21
22
  *
22
23
  * @param {string} rawInner - The text between `[[` and `]]`.
24
+ * @param {string} [literal] - Whole matched link, including trailing attributes.
23
25
  * @returns {ParsedWikilink} The parts, each trimmed.
24
26
  */
25
- export function parseWikilink(rawInner: string): ParsedWikilink;
27
+ export function parseWikilink(rawInner: string, literal?: string): ParsedWikilink;
26
28
  /**
27
29
  * The label an author actually supplied, or `null` when they supplied none.
28
30
  *
@@ -158,13 +160,14 @@ export function ambiguousAddressMessage(target: string, packages?: Iterable<stri
158
160
  * @param {string} [finding.stub] - For `stub`, where the stub is.
159
161
  * @param {string} [finding.shortcode] - For `shortcode-not-lowercase`, the
160
162
  * shortcode as written.
163
+ * @param {string[]} [finding.problems] - Invalid attribute diagnostics.
161
164
  * @param {string} [finding.declared] - For `shortcode-not-lowercase`, the
162
165
  * lowercase spelling, when it is a declared shortcode.
163
166
  * @returns {string} The message.
164
167
  * @throws {Error} On a reason outside the closed set — a resolver inventing one
165
168
  * would otherwise report a link with no explanation at all.
166
169
  */
167
- export function linkFindingMessage({ reason, target, packages, anchor, type, stub, shortcode, declared, }: {
170
+ export function linkFindingMessage({ reason, target, packages, anchor, type, stub, shortcode, declared, problems, }: {
168
171
  reason: string;
169
172
  target: string;
170
173
  packages?: Iterable<string> | undefined;
@@ -172,6 +175,7 @@ export function linkFindingMessage({ reason, target, packages, anchor, type, stu
172
175
  type?: string | undefined;
173
176
  stub?: string | undefined;
174
177
  shortcode?: string | undefined;
178
+ problems?: string[] | undefined;
175
179
  declared?: string | undefined;
176
180
  }): string;
177
181
  /**
@@ -188,9 +192,23 @@ export function isSamePage({ target, anchor }: ParsedWikilink): boolean;
188
192
  * Read an operational link target in its enclosing Address context.
189
193
  * @param {string} inner - Lexical link contents.
190
194
  * @param {object} context - Builder defaults and type vocabulary.
195
+ * @param {string} [literal] - Whole matched link, including trailing attributes.
191
196
  * @returns {object} Typed target and diagnostic source spelling.
192
197
  */
193
- export function readWikilink(inner: string, context: object): object;
198
+ export function readWikilink(inner: string, context: object, literal?: string): object;
199
+ /** Whether the actual referenced destination needs spoiler presentation.
200
+ * @param {object} destination - Resolved note metadata.
201
+ * @param {string} [anchor] - Referenced prose anchor.
202
+ * @returns {boolean} Whole-note or section audience.
203
+ */
204
+ export function secretDestination(destination: object, anchor?: string): boolean;
205
+ /** Apply destination-derived presentation without changing its address.
206
+ * @param {string} reference - Rendered reference Markdown.
207
+ * @param {object} parsed - Shared parsed wikilink.
208
+ * @param {object} destination - Resolved target metadata.
209
+ * @returns {string} Inline spoiler or visible reference.
210
+ */
211
+ export function presentWikilink(reference: string, parsed: object, destination: object): string;
194
212
  /**
195
213
  * What a `[[…]]` **is**, before anything decides where it points.
196
214
  *
@@ -297,6 +315,25 @@ export type ParsedWikilink = {
297
315
  * is unlabelled. `null` and `""` differ: an author may write `[[x|]]`.
298
316
  */
299
317
  display: string | null;
318
+ /**
319
+ * - Trailing extension attributes.
320
+ */
321
+ attributes?: {
322
+ id: string;
323
+ classes: string[];
324
+ values: Record<string, string>;
325
+ problems: string[];
326
+ positions?: Record<string, {
327
+ key: {
328
+ start: number;
329
+ end: number;
330
+ };
331
+ value: {
332
+ start: number;
333
+ end: number;
334
+ };
335
+ }>;
336
+ } | undefined;
300
337
  /**
301
338
  * - Whether a `|` was present at all. A link
302
339
  * without one addresses nothing and is a finding — see