@heroiclands/package-build 23.5.1 → 23.6.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 (43) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/bin/content-commands.mjs +23 -5
  3. package/docs/api.md +9 -0
  4. package/docs/authoring/links-and-markup.md +77 -12
  5. package/docs/content-format.md +16 -0
  6. package/docs/guides/reference-pages.md +89 -0
  7. package/docs/reference/format-details.md +533 -84
  8. package/docs/reference/note-types.md +191 -149
  9. package/engine/base-compiler.mjs +21 -0
  10. package/engine/content-format.yaml +9 -0
  11. package/engine/content-index.mjs +19 -5
  12. package/engine/culture-reach.mjs +330 -0
  13. package/engine/dictionary-layout.mjs +340 -0
  14. package/engine/dictionary-rows.mjs +608 -0
  15. package/engine/glossary-entries.mjs +182 -0
  16. package/engine/glossary-language.mjs +99 -0
  17. package/engine/index.mjs +1 -0
  18. package/engine/infobox.mjs +8 -0
  19. package/engine/language-words.mjs +360 -0
  20. package/engine/literature-notes.mjs +10 -8
  21. package/engine/note-addresses.mjs +25 -0
  22. package/engine/note-vocabulary.mjs +161 -1
  23. package/engine/path-charset.mjs +120 -0
  24. package/engine/sql-tables.mjs +311 -21
  25. package/engine/word-classes.mjs +167 -0
  26. package/engine/word-rows.mjs +233 -0
  27. package/engine/word-terms.mjs +306 -0
  28. package/package.json +1 -1
  29. package/types/engine/content-index.d.mts +15 -5
  30. package/types/engine/culture-reach.d.mts +66 -0
  31. package/types/engine/dictionary-layout.d.mts +69 -0
  32. package/types/engine/dictionary-rows.d.mts +39 -0
  33. package/types/engine/glossary-entries.d.mts +12 -0
  34. package/types/engine/glossary-language.d.mts +42 -0
  35. package/types/engine/index.d.mts +1 -0
  36. package/types/engine/language-words.d.mts +33 -0
  37. package/types/engine/literature-notes.d.mts +2 -2
  38. package/types/engine/note-addresses.d.mts +17 -0
  39. package/types/engine/path-charset.d.mts +38 -0
  40. package/types/engine/sql-tables.d.mts +7 -2
  41. package/types/engine/word-classes.d.mts +87 -0
  42. package/types/engine/word-rows.d.mts +64 -0
  43. package/types/engine/word-terms.d.mts +68 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @typedef {object} LayoutOptions
3
+ * @property {(ref: object) => boolean} [linkable] - Whether an Address can be
4
+ * linked to; a citation that cannot prints as its label.
5
+ * @property {number} [sectionLevel=2] - The level of a letter heading.
6
+ * @property {object} [addressContext] - Address resolution context.
7
+ * @property {(address: string) => string|undefined} [labelOf] - A note's
8
+ * `name.full`, by its Address.
9
+ * @property {string} [pageLanguage] - The language of the page the fence is
10
+ * on, which no entry is tagged with.
11
+ */
12
+ /**
13
+ * Render a result of words as a dictionary.
14
+ *
15
+ * @param {{columns: string[], rows: object[]}} result - From
16
+ * {@link module:engine/sql-tables.runSqlQuery}.
17
+ * @param {LayoutOptions} [opts]
18
+ * @returns {string} The Markdown.
19
+ * @throws {Error} When the result lacks a required column.
20
+ */
21
+ export function renderDictionary(result: {
22
+ columns: string[];
23
+ rows: object[];
24
+ }, opts?: LayoutOptions): string;
25
+ /**
26
+ * Render a result of terms as a glossary.
27
+ *
28
+ * @param {{columns: string[], rows: object[]}} result - From
29
+ * {@link module:engine/sql-tables.runSqlQuery}.
30
+ * @param {LayoutOptions} [opts]
31
+ * @returns {string} The Markdown.
32
+ * @throws {Error} When the result lacks a required column.
33
+ */
34
+ export function renderGlossary(result: {
35
+ columns: string[];
36
+ rows: object[];
37
+ }, opts?: LayoutOptions): string;
38
+ /** The columns a dictionary or a glossary cannot be set without. */
39
+ export const LAYOUT_REQUIRED_COLUMNS: Readonly<{
40
+ dictionary: readonly string[];
41
+ glossary: readonly string[];
42
+ }>;
43
+ /** The mark a common entry opens with. */
44
+ export const COMMON_MARK: "\u2022";
45
+ export type LayoutOptions = {
46
+ /**
47
+ * - Whether an Address can be
48
+ * linked to; a citation that cannot prints as its label.
49
+ */
50
+ linkable?: ((ref: object) => boolean) | undefined;
51
+ /**
52
+ * - The level of a letter heading.
53
+ */
54
+ sectionLevel?: number | undefined;
55
+ /**
56
+ * - Address resolution context.
57
+ */
58
+ addressContext?: object | undefined;
59
+ /**
60
+ * - A note's
61
+ * `name.full`, by its Address.
62
+ */
63
+ labelOf?: ((address: string) => string | undefined) | undefined;
64
+ /**
65
+ * - The language of the page the fence is
66
+ * on, which no entry is tagged with.
67
+ */
68
+ pageLanguage?: string | undefined;
69
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The heading letter of a sort key: its first letter or digit, accents taken
3
+ * off and apostrophes skipped, in capitals; `#` where it has none.
4
+ *
5
+ * @param {unknown} text - The sort key.
6
+ * @returns {string} The letter.
7
+ */
8
+ export function dictionaryLetter(text: unknown): string;
9
+ /**
10
+ * The `dictionary` and `terms` rows of a set of index records.
11
+ *
12
+ * @param {Iterable<Record<string, any>>} records - Index records, every
13
+ * Address a string, already narrowed to the audience being published to.
14
+ * @param {Iterable<Record<string, any>>} [all] - Every record of the package,
15
+ * for resolving a language or a culture through a note the audience does not
16
+ * see.
17
+ * @returns {{dictionary: object[], terms: object[]}} The rows: `dictionary`
18
+ * sorted by language, `name_sort` and name, `terms` by owner, `name_sort` and
19
+ * name, each carrying every column of {@link DICTIONARY_COLUMNS} or
20
+ * {@link TERM_COLUMNS}.
21
+ */
22
+ export function dictionaryAndTermRows(records: Iterable<Record<string, any>>, all?: Iterable<Record<string, any>>): {
23
+ dictionary: object[];
24
+ terms: object[];
25
+ };
26
+ /**
27
+ * Rows as a datafile holds them: one JSON object per line, keys sorted at
28
+ * every depth, newline-terminated; the empty string for none.
29
+ *
30
+ * @param {readonly object[]} rows - From {@link dictionaryAndTermRows}.
31
+ * @returns {string} The JSON Lines.
32
+ */
33
+ export function serializeRows(rows: readonly object[]): string;
34
+ /** Every column of the `dictionary` relation, in order. */
35
+ export const DICTIONARY_COLUMNS: readonly string[];
36
+ /** Every column of the `terms` relation, in order. */
37
+ export const TERM_COLUMNS: readonly string[];
38
+ /** The keys of a word's `etymology`, in order. */
39
+ export const ETYMOLOGY_KEYS: readonly string[];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Check a culture's or an affiliation's `data.glossary`.
3
+ *
4
+ * @param {object} note - A `lore` or `affiliation` note.
5
+ * @param {{index?: object}} [context] - The index, for what the note's terms
6
+ * and languages are. Without one, only what the note says of itself is
7
+ * checked.
8
+ * @returns {object[]} The findings.
9
+ */
10
+ export function checkGlossary(note: object, { index }?: {
11
+ index?: object;
12
+ }): object[];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * How a caller reads Addresses and finds notes.
3
+ *
4
+ * @typedef {object} GlossaryLanguageReader
5
+ * @property {(value: unknown, type: string) => string|undefined} keyOf - A
6
+ * value's canonical Address in the `note` system, read with `type` as the
7
+ * default type, or `undefined` where it is no Address.
8
+ * @property {(key: string) => object|undefined} lookup - The note an Address
9
+ * names, or `undefined` where none is known.
10
+ * @property {string} [self] - The owning note's own Address, for a language.
11
+ */
12
+ /**
13
+ * The language a glossary entry resolves to.
14
+ *
15
+ * @param {object|undefined} entry - The entry, or nothing for the note's own
16
+ * language.
17
+ * @param {object} owner - The note the entry is written on.
18
+ * @param {GlossaryLanguageReader} reader - How to read Addresses and notes.
19
+ * @returns {string|undefined} The language's Address, or `undefined` where the
20
+ * chain finds none.
21
+ */
22
+ export function glossaryLanguage(entry: object | undefined, owner: object, { keyOf, lookup, self }: GlossaryLanguageReader): string | undefined;
23
+ /**
24
+ * How a caller reads Addresses and finds notes.
25
+ */
26
+ export type GlossaryLanguageReader = {
27
+ /**
28
+ * - A
29
+ * value's canonical Address in the `note` system, read with `type` as the
30
+ * default type, or `undefined` where it is no Address.
31
+ */
32
+ keyOf: (value: unknown, type: string) => string | undefined;
33
+ /**
34
+ * - The note an Address
35
+ * names, or `undefined` where none is known.
36
+ */
37
+ lookup: (key: string) => object | undefined;
38
+ /**
39
+ * - The owning note's own Address, for a language.
40
+ */
41
+ self?: string | undefined;
42
+ };
@@ -60,6 +60,7 @@ export * as siteBuild from "./site-build.mjs";
60
60
  export * as sitePreview from "./site-preview.mjs";
61
61
  export * as contentLint from "./content-lint.mjs";
62
62
  export * as contentCharset from "./content-charset.mjs";
63
+ export * as pathCharset from "./path-charset.mjs";
63
64
  export * as contentIcons from "./content-icons.mjs";
64
65
  export * as contentHtml from "./content-html.mjs";
65
66
  export * as contentImages from "./content-images.mjs";
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Check a `skill` note's words against what they mean.
3
+ *
4
+ * @param {object} note - A `skill` note.
5
+ * @param {{index?: object}} [context] - The index, for whether an Address
6
+ * resolves. Without one, only what the note says of itself is checked.
7
+ * @returns {object[]} The findings.
8
+ */
9
+ export function checkLanguageWords(note: object, { index }?: {
10
+ index?: object;
11
+ }): object[];
12
+ /**
13
+ * Check a `skill` note's `data.abbreviation`: an error on a skill of another
14
+ * subType, at the key, and on a blank value, at the value.
15
+ *
16
+ * @param {object} note - A `skill` note.
17
+ * @returns {object[]} The findings.
18
+ */
19
+ export function checkLanguageAbbreviation(note: object): object[];
20
+ /**
21
+ * Check a `skill` note's `data.family`.
22
+ *
23
+ * An Address that does not resolve is the reference check's to report, so this
24
+ * speaks about a key on the wrong subType, a target that is not a language, and
25
+ * a chain of parents that returns to this note.
26
+ *
27
+ * @param {object} note - A `skill` note.
28
+ * @param {{index?: object}} [context] - The index, for what the chain reaches.
29
+ * @returns {object[]} The findings.
30
+ */
31
+ export function checkLanguageFamily(note: object, { index }?: {
32
+ index?: object;
33
+ }): object[];
@@ -25,8 +25,8 @@ export const LITERATURE_SUBTYPE: "literature";
25
25
  /** The `lore` subTypes that may name a language. */
26
26
  export const LANGUAGE_SUBTYPES: readonly string[];
27
27
  /**
28
- * `data.language`: the tongue a work is composed in, or the tongue a culture's
29
- * people speak. Optional on both.
28
+ * `data.language`: the tongue a work is composed in, the tongue a culture's
29
+ * people speak, or the tongue an affiliation keeps. Optional on all three.
30
30
  *
31
31
  * @type {object}
32
32
  */
@@ -57,12 +57,20 @@ export function decodeIndexAddresses(record: object, context: object, source?: {
57
57
  }): object;
58
58
  /** Structured positions whose Address is nested inside a declared container. */
59
59
  export const STRUCTURED_ADDRESSES: readonly ({
60
+ noteType: string;
61
+ path: string[];
62
+ shape: string;
63
+ type: string;
64
+ accepts: string[];
65
+ lenient: boolean;
66
+ } | {
60
67
  path: string[];
61
68
  shape: string;
62
69
  type: string;
63
70
  accepts: string[];
64
71
  system: string;
65
72
  noteType?: undefined;
73
+ lenient?: undefined;
66
74
  } | {
67
75
  noteType: string;
68
76
  path: string[];
@@ -70,5 +78,14 @@ export const STRUCTURED_ADDRESSES: readonly ({
70
78
  type: string;
71
79
  accepts: string[];
72
80
  system?: undefined;
81
+ lenient?: undefined;
82
+ } | {
83
+ noteType: string;
84
+ path: string[];
85
+ shape: string;
86
+ lenient: boolean;
87
+ type?: undefined;
88
+ accepts?: undefined;
89
+ system?: undefined;
73
90
  })[];
74
91
  export { AddressEntries, encodeAddresses, cloneAddressState, publishAddresses, readPublishedAddresses, flattenPublishedAddresses } from "./address-values.mjs";
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The distinct characters of a string outside U+0020 to U+007E, in order of
3
+ * first appearance.
4
+ *
5
+ * @param {string} text - A path, or one component of one.
6
+ * @returns {string[]} The offending characters; empty when the text is clean.
7
+ */
8
+ export function offendingPathCharacters(text: string): string[];
9
+ /**
10
+ * Walk the content tree and the package's picture roots, reporting each entry
11
+ * whose name holds a character outside printable ASCII.
12
+ *
13
+ * An entry is checked by its own name as the walk reaches it, so a directory
14
+ * with a bad name is reported once and not again for every file beneath it.
15
+ * Dot-entries and `skipDirectories` are skipped, as in the other content walks.
16
+ * A finding names its file relative to the working directory, with no line or
17
+ * column.
18
+ *
19
+ * @param {object} opts - Options.
20
+ * @param {string} opts.contentBase - Root of the content tree.
21
+ * @param {string} opts.assetsBase - The package's asset directory, whose
22
+ * picture roots are walked.
23
+ * @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore.
24
+ * @returns {{findings: Array<{file: string, severity: "error", message: string}>,
25
+ * files: number}} The findings, and how many paths were checked.
26
+ */
27
+ export function lintPathCharset({ contentBase, assetsBase, skipDirectories }: {
28
+ contentBase: string;
29
+ assetsBase: string;
30
+ skipDirectories?: readonly string[] | undefined;
31
+ }): {
32
+ findings: Array<{
33
+ file: string;
34
+ severity: "error";
35
+ message: string;
36
+ }>;
37
+ files: number;
38
+ };
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @param {string} markdown - The note body, frontmatter already stripped.
9
9
  * @returns {Array<{line: number, close: number, indent: string, query: string,
10
- * allowEmpty: boolean, sectionLevel: number, block: string}>} One entry per
10
+ * allowEmpty: boolean, sectionLevel: number, layout: string, block: string}>} One entry per
11
11
  * fence, in document order.
12
12
  */
13
13
  export function findSqlBlocks(markdown: string): Array<{
@@ -17,6 +17,7 @@ export function findSqlBlocks(markdown: string): Array<{
17
17
  query: string;
18
18
  allowEmpty: boolean;
19
19
  sectionLevel: number;
20
+ layout: string;
20
21
  block: string;
21
22
  }>;
22
23
  /**
@@ -142,6 +143,9 @@ export function renderSqlTable(result: {
142
143
  * @param {object} [opts]
143
144
  * @param {(ref: string) => boolean} [opts.linkable] - Passed to
144
145
  * {@link renderSqlTable}.
146
+ * @param {(source: string, frontmatter?: object) => string|undefined} [opts.pageLanguage] -
147
+ * The language of the note a body belongs to, which a dictionary or a
148
+ * glossary does not tag; absent, it is read from the body's frontmatter.
145
149
  * @param {object[]} [opts.records] - Content-index records, narrowed to the
146
150
  * audience this surface publishes to. Given them, the result also carries
147
151
  * `pageLists`, so one prepared object answers every corpus-reading directive
@@ -158,8 +162,9 @@ export function renderSqlTable(result: {
158
162
  export function prepareSqlTables(db: object, sources: Array<{
159
163
  source: string;
160
164
  markdown: string;
161
- }>, { linkable, records }?: {
165
+ }>, { linkable, records, pageLanguage }?: {
162
166
  linkable?: ((ref: string) => boolean) | undefined;
167
+ pageLanguage?: ((source: string, frontmatter?: object) => string | undefined) | undefined;
163
168
  records?: object[] | undefined;
164
169
  }): Promise<Map<string, object[]>>;
165
170
  /** Evaluate scalar SQL calls before synchronous Markdown renderers run. */
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Reads a class string as a base and its modifiers.
3
+ *
4
+ * The result is `{ base, modifiers }` when the string is a class, and
5
+ * `{ reason }` when it is not. A hyphen is not a separator: `n-coll` is
6
+ * refused, and `n coll` is the collective noun.
7
+ *
8
+ * @param {string} text
9
+ * @returns {{ base: string, modifiers: string[], reason?: undefined } | { reason: string, base?: undefined, modifiers?: undefined }}
10
+ */
11
+ export function parseWordClass(text: string): {
12
+ base: string;
13
+ modifiers: string[];
14
+ reason?: undefined;
15
+ } | {
16
+ reason: string;
17
+ base?: undefined;
18
+ modifiers?: undefined;
19
+ };
20
+ /**
21
+ * The dictionary abbreviation of a parsed class, such as `n. I f.`. A base
22
+ * with no abbreviation prints as its token.
23
+ *
24
+ * @param {{ base: string, modifiers: readonly string[] }} parsed
25
+ * @returns {string}
26
+ */
27
+ export function abbreviateWordClass(parsed: {
28
+ base: string;
29
+ modifiers: readonly string[];
30
+ }): string;
31
+ /**
32
+ * A parsed class in plain words, such as "noun of class I, feminine".
33
+ *
34
+ * @param {{ base: string, modifiers: readonly string[] }} parsed
35
+ * @returns {string}
36
+ */
37
+ export function expandWordClass(parsed: {
38
+ base: string;
39
+ modifiers: readonly string[];
40
+ }): string;
41
+ /** The bases, in glossary order. */
42
+ export const WORD_CLASS_BASES: readonly Readonly<WordClassBase>[];
43
+ /**
44
+ * The modifiers. The neuter prints `nt.` because `n. n.` would show the base
45
+ * and the gender identically.
46
+ */
47
+ export const WORD_CLASS_MODIFIERS: readonly Readonly<WordClassModifier>[];
48
+ export type WordClassBase = {
49
+ /**
50
+ * The base as a word writes it.
51
+ */
52
+ token: string;
53
+ /**
54
+ * The dictionary abbreviation, or
55
+ * `null` for a base that never prints in the glossary.
56
+ */
57
+ abbreviation: string | null;
58
+ /**
59
+ * The base in plain words.
60
+ */
61
+ expansion: string;
62
+ /**
63
+ * Whether entries of this base appear in the
64
+ * glossary.
65
+ */
66
+ glossary: boolean;
67
+ };
68
+ export type WordClassModifier = {
69
+ /**
70
+ * The modifier as a word writes it.
71
+ */
72
+ token: string;
73
+ /**
74
+ * What the modifier says: `gender`, `animacy`,
75
+ * `number`, `declension`, `conjugation` or `voice`.
76
+ */
77
+ group: string;
78
+ /**
79
+ * The dictionary abbreviation.
80
+ */
81
+ abbreviation: string;
82
+ /**
83
+ * The modifier in plain words; a phrase starting
84
+ * `of` follows its base without a comma.
85
+ */
86
+ expansion: string;
87
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A lookup from a built piece to its gloss, over one language's words (stems
3
+ * included) and affixes.
4
+ *
5
+ * A piece matches a word or an affix written exactly as it is, and failing
6
+ * that one written with the same letters and different joining hyphens, so
7
+ * `wulf-` finds the word `wulf`. A word's own gloss wins over an affix's.
8
+ *
9
+ * @param {object} data - The language note's `data`.
10
+ * @returns {(piece: string) => string|null} The gloss, or `null` where none resolves.
11
+ */
12
+ export function pieceGlosses(data: object): (piece: string) => string | null;
13
+ /**
14
+ * Whether a record is a language note's.
15
+ *
16
+ * @param {Record<string, any>} record - An index record.
17
+ * @returns {boolean}
18
+ */
19
+ export function isLanguageRecord(record: Record<string, any>): boolean;
20
+ /**
21
+ * The `words` rows of a set of index records.
22
+ *
23
+ * A row's `language` is the note that states it.
24
+ *
25
+ * @param {Iterable<Record<string, any>>} records - Index records, every
26
+ * Address a string, already narrowed to the audience being published to.
27
+ * @returns {object[]} One row per word, in record then entry order.
28
+ */
29
+ export function wordRows(records: Iterable<Record<string, any>>): object[];
30
+ /**
31
+ * The `glossary` rows of a set of index records: one per entry of a culture's
32
+ * or an affiliation's `data.glossary`, with `name_sort` the name and `common`
33
+ * `false` where the entry leaves them unstated, and `language` the language
34
+ * the entry resolves to, or `NULL` where it resolves to none.
35
+ *
36
+ * @param {Iterable<Record<string, any>>} records - Index records, every
37
+ * Address a string, already narrowed to the audience being published to.
38
+ * @param {Iterable<Record<string, any>>} [all] - Every record of the package,
39
+ * for resolving a language through a note the audience does not see.
40
+ * @returns {object[]} One row per entry, in record then entry order.
41
+ */
42
+ export function glossaryRows(records: Iterable<Record<string, any>>, all?: Iterable<Record<string, any>>): object[];
43
+ /**
44
+ * The `language_lineage` rows of a set of index records: for each language
45
+ * note, itself at depth `0`, its `data.family` at depth `1`, and so on up the
46
+ * chain. The chain stops at a parent that is no language note of the package,
47
+ * and before a language it has already reached, so a cycle the lint refuses
48
+ * still yields finite rows.
49
+ *
50
+ * @param {Iterable<Record<string, any>>} records - Index records, every
51
+ * Address a string, already narrowed to the audience being published to.
52
+ * @param {Iterable<Record<string, any>>} [all] - Every record of the package,
53
+ * for following a chain through a note the audience does not see.
54
+ * @returns {object[]} One row per language and ancestor, in record then depth order.
55
+ */
56
+ export function lineageRows(records: Iterable<Record<string, any>>, all?: Iterable<Record<string, any>>): object[];
57
+ /** Every column of the `words` relation, in order. */
58
+ export const WORD_COLUMNS: readonly string[];
59
+ /** Every column of the `glossary` relation, in order. */
60
+ export const GLOSSARY_COLUMNS: readonly string[];
61
+ /** Every column of the `language_lineage` relation, in order. */
62
+ export const LINEAGE_COLUMNS: readonly string[];
63
+ /** The columns of any of these relations that hold a whole number. */
64
+ export const ROW_INTEGER_COLUMNS: readonly string[];
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Why a pronunciation is not a respelling, or `undefined` when it is one.
3
+ *
4
+ * A respelling is letters and apostrophes in hyphen-separated syllables, with
5
+ * at most one syllable in capitals.
6
+ *
7
+ * @param {string} text
8
+ * @returns {string|undefined}
9
+ */
10
+ export function pronunciationProblem(text: string): string | undefined;
11
+ /**
12
+ * The inner keys of a language note's words, fields and affixes, and of a
13
+ * culture's or an affiliation's glossary entries, as inner-key declarations.
14
+ *
15
+ * A leaf module, importing nothing: the note vocabulary reads these
16
+ * declarations as it loads, and the checks that judge what the values mean —
17
+ * a class against the master word classes, a field against the note's own
18
+ * fields — live in `language-words.mjs` and `glossary-entries.mjs`.
19
+ *
20
+ * @module
21
+ */
22
+ /** The `skill` subType whose notes are languages, and the only one with words. */
23
+ export const LANGUAGE_SUBTYPE: "language";
24
+ /** The `data` keys a language note states its words in, beside `names`. */
25
+ export const WORD_DATA_KEYS: readonly string[];
26
+ /**
27
+ * Where a word comes from. `borrowed` names the tongue it was taken from;
28
+ * `inherited`, `kept` and `worn` name the ancient form where the note gives
29
+ * one; `coined`, `substrate` and `island` name no form.
30
+ */
31
+ export const WORD_SOURCE_KINDS: readonly string[];
32
+ /** The source kind that requires `language`. */
33
+ export const BORROWED_SOURCE_KIND: "borrowed";
34
+ /**
35
+ * The keys of a word's `source`.
36
+ *
37
+ * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
38
+ */
39
+ export const WORD_SOURCE_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
40
+ /**
41
+ * The keys one word of a language note's `data.words` carries.
42
+ *
43
+ * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
44
+ */
45
+ export const WORD_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
46
+ /** The keys of {@link WORD_FIELDS}, in declared order. */
47
+ export const WORD_KEYS: readonly string[];
48
+ /**
49
+ * The keys one entry of a language note's `data.fields` carries.
50
+ *
51
+ * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
52
+ */
53
+ export const WORD_FIELD_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
54
+ /**
55
+ * The keys one entry of a language note's `data.affixes` carries.
56
+ *
57
+ * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
58
+ */
59
+ export const AFFIX_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
60
+ /**
61
+ * The keys one entry of a culture's or an affiliation's `data.glossary`
62
+ * carries.
63
+ *
64
+ * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
65
+ */
66
+ export const GLOSSARY_ENTRY_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
67
+ /** The keys of {@link GLOSSARY_ENTRY_FIELDS}, in declared order. */
68
+ export const GLOSSARY_ENTRY_KEYS: readonly string[];