@heroiclands/package-build 17.1.0 → 18.0.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 (153) hide show
  1. package/CHANGELOG.md +1773 -0
  2. package/CONTENT.md +288 -72
  3. package/README.md +1 -1
  4. package/bin/content-build.mjs +218 -137
  5. package/bin/package-build.mjs +43 -0
  6. package/content-config.mjs +62 -67
  7. package/docs/content-format.md +768 -58
  8. package/engine/actor-compiler.mjs +586 -0
  9. package/engine/address-charset.mjs +11 -8
  10. package/engine/address-diff.mjs +266 -22
  11. package/engine/anchored-sections.mjs +83 -0
  12. package/engine/anchors.mjs +83 -0
  13. package/engine/base-compiler.mjs +148 -18
  14. package/engine/bundle-notes.mjs +276 -0
  15. package/engine/bundles.mjs +307 -0
  16. package/engine/code-fences.mjs +103 -0
  17. package/engine/compile-corpus.mjs +89 -0
  18. package/engine/content-address.mjs +267 -5
  19. package/engine/content-format-check.mjs +54 -11
  20. package/engine/content-format.mjs +37 -5
  21. package/engine/content-index.mjs +394 -111
  22. package/engine/content-links.mjs +185 -77
  23. package/engine/content-lint.mjs +232 -14
  24. package/engine/content-package.mjs +2 -1
  25. package/engine/content-tables.mjs +99 -3
  26. package/engine/document-subtypes.mjs +49 -6
  27. package/engine/field-reference.mjs +1 -1
  28. package/engine/field-spec.mjs +56 -6
  29. package/engine/folder-notes.mjs +470 -0
  30. package/engine/foreign-catalog.mjs +195 -4
  31. package/engine/{manifest-emit.mjs → foundry-entries.mjs} +78 -81
  32. package/engine/frontmatter-lint.mjs +245 -19
  33. package/engine/frontmatter.mjs +88 -8
  34. package/engine/generate.mjs +304 -13
  35. package/engine/helpers.mjs +293 -64
  36. package/engine/ids.mjs +137 -2
  37. package/engine/index-records.mjs +126 -0
  38. package/engine/index.mjs +23 -5
  39. package/engine/item-compiler.mjs +349 -0
  40. package/engine/item-docs.mjs +7 -1
  41. package/engine/item-registry.mjs +6 -0
  42. package/engine/journals.mjs +93 -27
  43. package/engine/macros.mjs +4 -2
  44. package/engine/metadata-index.mjs +495 -0
  45. package/engine/note-claims.mjs +146 -25
  46. package/engine/note-ids.mjs +112 -0
  47. package/engine/note-renames.mjs +134 -0
  48. package/engine/note-schemas.mjs +25 -0
  49. package/engine/note-vocabulary.mjs +110 -27
  50. package/engine/pack-config.mjs +4 -0
  51. package/engine/pack-router.mjs +23 -0
  52. package/engine/retired-fields.mjs +137 -3
  53. package/engine/scenes.mjs +66 -34
  54. package/engine/schema-check.mjs +58 -17
  55. package/engine/site-build.mjs +71 -12
  56. package/engine/site-index.mjs +20 -7
  57. package/engine/sql-tables.mjs +485 -0
  58. package/engine/subtype-registry.mjs +102 -0
  59. package/engine/system-block.mjs +96 -12
  60. package/engine/systems.mjs +201 -0
  61. package/engine/web-wikilinks.mjs +50 -20
  62. package/engine/wikilink-syntax.mjs +17 -8
  63. package/engine/wikilinks.mjs +124 -51
  64. package/engine/yaml-lint.mjs +282 -0
  65. package/hm3/actors.mjs +352 -0
  66. package/hm3/default-item-art.mjs +75 -0
  67. package/hm3/document-subtypes.mjs +134 -0
  68. package/hm3/index.mjs +56 -0
  69. package/hm3/item-builders.mjs +84 -0
  70. package/hm3/item-fields.mjs +180 -0
  71. package/hm3/items.mjs +96 -0
  72. package/hm3/template-priority.mjs +85 -0
  73. package/labels.mjs +191 -0
  74. package/manifest.mjs +43 -2
  75. package/package.json +19 -4
  76. package/release.mjs +62 -7
  77. package/sohl/actors.mjs +33 -487
  78. package/sohl/being-info.mjs +16 -7
  79. package/sohl/default-item-art.mjs +14 -3
  80. package/sohl/document-subtypes.mjs +16 -10
  81. package/sohl/item-builders.mjs +14 -5
  82. package/sohl/item-fields.mjs +68 -7
  83. package/sohl/items.mjs +44 -258
  84. package/sohl/note-schemas.mjs +2 -2
  85. package/types/content-config.d.mts +21 -42
  86. package/types/engine/actor-compiler.d.mts +204 -0
  87. package/types/engine/address-charset.d.mts +11 -8
  88. package/types/engine/address-diff.d.mts +53 -5
  89. package/types/engine/anchored-sections.d.mts +21 -0
  90. package/types/engine/anchors.d.mts +20 -0
  91. package/types/engine/base-compiler.d.mts +17 -17
  92. package/types/engine/bundle-notes.d.mts +173 -0
  93. package/types/engine/bundles.d.mts +60 -0
  94. package/types/engine/code-fences.d.mts +43 -0
  95. package/types/engine/compile-corpus.d.mts +32 -0
  96. package/types/engine/content-address.d.mts +205 -5
  97. package/types/engine/content-format-check.d.mts +6 -2
  98. package/types/engine/content-format.d.mts +57 -1
  99. package/types/engine/content-index.d.mts +64 -80
  100. package/types/engine/content-links.d.mts +52 -7
  101. package/types/engine/content-lint.d.mts +10 -1
  102. package/types/engine/content-package.d.mts +2 -1
  103. package/types/engine/content-tables.d.mts +21 -39
  104. package/types/engine/document-subtypes.d.mts +37 -3
  105. package/types/engine/field-spec.d.mts +76 -5
  106. package/types/engine/folder-notes.d.mts +159 -0
  107. package/types/engine/foreign-catalog.d.mts +53 -0
  108. package/types/engine/{manifest-emit.d.mts → foundry-entries.d.mts} +16 -38
  109. package/types/engine/frontmatter-lint.d.mts +10 -2
  110. package/types/engine/frontmatter.d.mts +64 -0
  111. package/types/engine/generate.d.mts +38 -0
  112. package/types/engine/helpers.d.mts +94 -30
  113. package/types/engine/ids.d.mts +96 -0
  114. package/types/engine/index-records.d.mts +68 -0
  115. package/types/engine/index.d.mts +9 -3
  116. package/types/engine/item-compiler.d.mts +131 -0
  117. package/types/engine/journals.d.mts +47 -9
  118. package/types/engine/metadata-index.d.mts +226 -0
  119. package/types/engine/note-claims.d.mts +57 -11
  120. package/types/engine/note-ids.d.mts +38 -0
  121. package/types/engine/note-renames.d.mts +102 -0
  122. package/types/engine/note-vocabulary.d.mts +44 -8
  123. package/types/engine/retired-fields.d.mts +75 -0
  124. package/types/engine/scenes.d.mts +3 -2
  125. package/types/engine/schema-check.d.mts +25 -4
  126. package/types/engine/site-build.d.mts +4 -4
  127. package/types/engine/site-index.d.mts +1 -1
  128. package/types/engine/sql-tables.d.mts +185 -0
  129. package/types/engine/subtype-registry.d.mts +49 -0
  130. package/types/engine/system-block.d.mts +40 -1
  131. package/types/engine/systems.d.mts +106 -0
  132. package/types/engine/web-wikilinks.d.mts +4 -2
  133. package/types/engine/wikilink-syntax.d.mts +10 -3
  134. package/types/engine/wikilinks.d.mts +41 -13
  135. package/types/engine/yaml-lint.d.mts +107 -0
  136. package/types/hm3/actors.d.mts +48 -0
  137. package/types/hm3/default-item-art.d.mts +42 -0
  138. package/types/hm3/document-subtypes.d.mts +24 -0
  139. package/types/hm3/index.d.mts +7 -0
  140. package/types/hm3/item-builders.d.mts +11 -0
  141. package/types/hm3/item-fields.d.mts +12 -0
  142. package/types/hm3/items.d.mts +23 -0
  143. package/types/hm3/template-priority.d.mts +21 -0
  144. package/types/manifest.d.mts +22 -1
  145. package/types/release.d.mts +9 -4
  146. package/types/sohl/actors.d.mts +7 -74
  147. package/types/sohl/being-info.d.mts +8 -27
  148. package/types/sohl/default-item-art.d.mts +5 -3
  149. package/types/sohl/items.d.mts +17 -32
  150. package/engine/foreign-manifests.mjs +0 -126
  151. package/engine/kb-manifest.mjs +0 -490
  152. package/types/engine/foreign-manifests.d.mts +0 -43
  153. package/types/engine/kb-manifest.d.mts +0 -241
@@ -1,36 +1,20 @@
1
- /**
2
- * The `{#slug}` anchors a note's body declares, with where each one sits.
3
- *
4
- * Only headings carrying an explicit anchor are collected. A bare `#` heading
5
- * also starts a journal page, but it declares no slug, so nothing can address
6
- * it with `#…` — listing it would offer a link that cannot be written.
7
- *
8
- * @param {string} body - The note's markdown body, frontmatter already removed.
9
- * @param {number} [bodyLine] - The 1-based file line the body starts on, from
10
- * `parseMarkdownFile`. Anchors are reported at their position in the **file**,
11
- * so an editor can jump straight to one; passing nothing numbers from the body.
12
- * @returns {Array<{slug: string, name: string, level: number, line: number}>}
13
- * In document order.
14
- */
15
- export function collectAnchors(body: string, bodyLine?: number): Array<{
16
- slug: string;
17
- name: string;
18
- level: number;
19
- line: number;
20
- }>;
21
1
  /**
22
2
  * The address a wikilink writes to reach a note, or `null` when it has none.
23
3
  *
24
4
  * A wikilink target is an address: `being-aurochs` locally, or
25
5
  * `sohl-being-aurochs` from another package (`readQualifier` also accepts
26
- * `being/aurochs`, the same two fields with a different separator). Both forms
27
- * are already derivable from `type` and `shortcode`, which every record
28
- * carries so this field adds no information. What it adds is the *rule*:
29
- * the lowercasing and the hyphen join live in one place, and a consumer that
30
- * reimplements them slightly differently gets a lookup that matches nothing and
31
- * says nothing about why. That is a real failure, not a hypothetical one it
32
- * is precisely how a resolver keyed on a bare `type/shortcode` silently misses
33
- * every canonical `pkg-type-shortcode` entry.
6
+ * `being/aurochs`, the same two fields with a different separator). Both are
7
+ * *partial* addresses the canonical one this field records is
8
+ * `sohl-sohl-being-aurochs`, and a target that omits the system is matched
9
+ * with that segment wildcarded. Every form is already derivable from `type` and
10
+ * `shortcode`, which every record carries, plus the system the type compiles
11
+ * into so this field adds no information. What it adds is the *rule*: the
12
+ * lowercasing, the hyphen join and the system lookup live in one place, and a
13
+ * consumer that reimplements them slightly differently gets a lookup that
14
+ * matches nothing and says nothing about why. That is a real failure, not a
15
+ * hypothetical one — it is precisely how a resolver keyed on a bare
16
+ * `type/shortcode` silently misses every canonical
17
+ * `pkg-system-type-shortcode` entry.
34
18
  *
35
19
  * Derived by the same functions the link manifest and the site build use, so an
36
20
  * index cannot disagree with either about where a note lives.
@@ -38,8 +22,8 @@ export function collectAnchors(body: string, bodyLine?: number): Array<{
38
22
  * @param {Record<string, any>} frontmatter - The note's parsed frontmatter.
39
23
  * @param {string} contentPackage - The package the tree compiles as.
40
24
  * @returns {{slug: string, canonical: string}|null} `slug` is what goes inside
41
- * `[[…]]` within this package; `canonical` is the package-qualified key the
42
- * manifest files the note under. `null` for a note with no type or no
25
+ * `[[…]]` within this package; `canonical` is the fully qualified key the
26
+ * manifest files the note under, carrying the package and the system as well. `null` for a note with no type or no
43
27
  * shortcode, which has no address at all and is stated as such rather than
44
28
  * left for every reader to rediscover.
45
29
  */
@@ -107,40 +91,21 @@ export function asciiName(name: unknown): string | null;
107
91
  * a consumer iterating it should not have to check first.
108
92
  */
109
93
  export function asciiAliases(aliases: unknown): Array<string>;
110
- /**
111
- * Build one index record from a note's frontmatter and its place in the tree.
112
- *
113
- * @param {object} options - Options.
114
- * @param {Record<string, any>} options.frontmatter - The note's parsed frontmatter.
115
- * @param {string} options.relPath - Its path below the content root, POSIX-separated.
116
- * @param {string} options.contentPackage - The package the tree compiles as.
117
- * @param {string} [options.body] - The note's markdown body, for its anchors.
118
- * @param {number} [options.bodyLine] - The 1-based file line the body starts on.
119
- * @returns {Record<string, any>} The record, keys sorted at every depth.
120
- * @throws {Error} When the note carries a key this module derives, which would
121
- * otherwise be overwritten without a word.
122
- */
123
- export function buildIndexRecord({ frontmatter, relPath, contentPackage, body, bodyLine }: {
124
- frontmatter: Record<string, any>;
125
- relPath: string;
126
- contentPackage: string;
127
- body?: string | undefined;
128
- bodyLine?: number | undefined;
94
+ export function buildIndexRecord({ frontmatter, relPath, absPath, contentPackage, body, bodyLine, manifest, }: {
95
+ frontmatter: any;
96
+ relPath: any;
97
+ absPath: any;
98
+ contentPackage: any;
99
+ body: any;
100
+ bodyLine: any;
101
+ manifest: any;
129
102
  }): Record<string, any>;
130
- /**
131
- * Read a content tree into index records, in the order they will be written.
132
- *
133
- * @param {string} contentBase - The content tree to walk.
134
- * @param {object} options - Options.
135
- * @param {string} options.contentPackage - The package the tree compiles as.
136
- * @param {Array<string>} [options.skipDirectories] - Directory names to skip.
137
- * @returns {Array<Record<string, any>>} The records, in a total order that does
138
- * not depend on directory-read order.
139
- */
140
- export function collectContentIndex(contentBase: string, { contentPackage, skipDirectories }: {
141
- contentPackage: string;
142
- skipDirectories?: string[] | undefined;
143
- }): Array<Record<string, any>>;
103
+ export function collectContentIndex(contentBase: any, { contentPackage, skipDirectories, manifest, problems }: {
104
+ contentPackage: any;
105
+ skipDirectories: any;
106
+ manifest: any;
107
+ problems: any;
108
+ }): Record<string, any>[];
144
109
  /**
145
110
  * Serialize records as JSON Lines.
146
111
  *
@@ -150,6 +115,36 @@ export function collectContentIndex(contentBase: string, { contentPackage, skipD
150
115
  * the file is exactly the lines it holds.
151
116
  */
152
117
  export function serializeContentIndex(records: Array<Record<string, any>>): string;
118
+ /**
119
+ * The index records for a content tree, without writing anything.
120
+ *
121
+ * The half of {@link emitContentIndex} that derives rather than emits, so a
122
+ * pass that needs the corpus in memory — a SQL content table, the link check,
123
+ * and in time every reader #243 converts — builds it the same way the artifact
124
+ * is built, rather than by walking and parsing again with its own idea of the
125
+ * scope.
126
+ *
127
+ * @param {object} [opts]
128
+ * @param {string} [opts.contentBase] - The tree, defaulting to the configured one.
129
+ * @param {object} [opts.config] - Resolved configuration, defaulting to ambient.
130
+ * @param {readonly string[]} [opts.skipDirectories] - The walk's scope, for a
131
+ * caller that resolved one of its own; defaults to the resolved
132
+ * configuration's. Stated separately from `config` because a caller that was
133
+ * *handed* a scope must be able to pass it on rather than have it silently
134
+ * replaced by the one its configuration happens to carry (#243).
135
+ * @param {object[]} [opts.problems] - Supplied by a **reader**: a note that
136
+ * cannot be recorded is pushed here as a diagnostic and skipped, instead of
137
+ * aborting the derivation. Omitted, the note throws — which is the contract
138
+ * the emitter needs, since an index missing a note asserts that it does not
139
+ * exist.
140
+ * @returns {object[]} One record per note, plus one per documentation entry.
141
+ */
142
+ export function indexRecordsFor({ contentBase, config, skipDirectories, problems }?: {
143
+ contentBase?: string | undefined;
144
+ config?: object | undefined;
145
+ skipDirectories?: readonly string[] | undefined;
146
+ problems?: object[] | undefined;
147
+ }): object[];
153
148
  /**
154
149
  * Emit this package's content index.
155
150
  *
@@ -175,20 +170,9 @@ export function emitContentIndex({ contentBase, outDir, config }?: {
175
170
  notes: number;
176
171
  bytes: number;
177
172
  };
178
- /**
179
- * The keys this module adds to a record, which a note therefore may not carry
180
- * itself.
181
- *
182
- * `package` is the note's distribution unit — the configured `contentPackage`,
183
- * since a note declaring its own is a hard error (package-build#56) — and it
184
- * matches what the content-table expander puts on the same field, so a query
185
- * reads the same value from either. `file` namespaces the note's place in the
186
- * tree, again matching the expander's `file.*`.
187
- *
188
- * Both are checked rather than assumed: `folder` is real frontmatter on most
189
- * notes, so the neighbouring names are close enough to a real key that a silent
190
- * overwrite is a plausible future rather than a hypothetical one.
191
- *
192
- * @type {ReadonlyArray<string>}
193
- */
194
- export const DERIVED_KEYS: ReadonlyArray<string>;
173
+ import { collectAnchors } from "./anchors.mjs";
174
+ import { authoredFrontmatter } from "./index-records.mjs";
175
+ import { DERIVED_KEYS } from "./index-records.mjs";
176
+ import { isNoteRecord } from "./index-records.mjs";
177
+ import { noteFile } from "./index-records.mjs";
178
+ export { collectAnchors, authoredFrontmatter, DERIVED_KEYS, isNoteRecord, noteFile };
@@ -1,12 +1,43 @@
1
1
  /**
2
2
  * Every `{#anchor}` a note declares on a heading.
3
3
  *
4
+ * **Read from the content index's reader, not a second one.** This module kept
5
+ * its own until #243, and the two disagreed: it matched `{#([a-z0-9-]+)}` while
6
+ * {@link module:engine/content-index.collectAnchors} matches `{#([^}]+)}`, so
7
+ * an anchor with a capital in it — `{#CalendarFormat}` — existed for the index
8
+ * and for the compiler and did not exist for the link checker. Nothing links to
9
+ * one today, so the disagreement was latent; the first link to one would have
10
+ * been reported dead against a heading plainly present in the file.
11
+ *
12
+ * The specification puts no charset on the id: "`#id` represents an id anchor
13
+ * named `id`". The narrower pattern was this module's invention, which is the
14
+ * argument for there being one reader rather than a well-chosen one.
15
+ *
4
16
  * @param {string} body - The note's markdown body.
5
17
  * @returns {Set<string>} The declared anchor slugs.
6
18
  */
7
19
  export function anchorsOf(body: string): Set<string>;
8
20
  /**
9
- * Load a content tree and build the index a link resolves against.
21
+ * Read a content tree into the index a link resolves against.
22
+ *
23
+ * **The corpus comes from the content index, not from a walk of this module's
24
+ * own** (#243). Every pass used to answer "which files are the content?" for
25
+ * itself and throw the answer away; this one now reads
26
+ * {@link module:engine/content-index.indexRecordsFor}, which is the same
27
+ * derivation the published artifact and the compilers are driven from. So a
28
+ * note the index records is a note the link check sees, and the addresses and
29
+ * anchors it resolves against are the ones every other pass will emit — rather
30
+ * than a second derivation that agrees with them only by inspection. That was
31
+ * not hypothetical: this module carried its own anchor reader until the anchor
32
+ * half of #243, and the two disagreed about which anchors existed.
33
+ *
34
+ * **The file is opened for its bytes and nothing else.** The index deliberately
35
+ * carries no note *body*, and a link lives in the body — so each note is read
36
+ * once, here, for the prose. Everything *about* the note — its frontmatter, its
37
+ * addresses, its anchors — is already in the record, and none of it is derived
38
+ * a second time. That is one read per note rather than the two this module did
39
+ * before, since the walk read the file and it then read it again for the raw
40
+ * text.
10
41
  *
11
42
  * The index mirrors what both builds construct, including the two addresses a
12
43
  * doc-carrying note answers to: `type/shortcode` for the document, and
@@ -17,14 +48,28 @@ export function anchorsOf(body: string): Set<string>;
17
48
  *
18
49
  * @param {string} contentBase - Root of the content tree.
19
50
  * @param {object} [opts]
20
- * @param {string} [opts.manifestDir] - Where vendored foreign manifests live.
21
- * Omitted, no cross-package address resolves.
22
- * @param {readonly string[]} [opts.skipDirectories] - Passed to the walk.
51
+ * @param {object} [opts.config] - The resolved build configuration, whose
52
+ * fetched dependency indexes foreign addresses resolve through (#239), and
53
+ * whose `contentPackage` every local address is built from. Omitted, the
54
+ * ambient configuration is resolved and no cross-package address resolves.
55
+ * @param {readonly string[]} [opts.skipDirectories] - The walk's scope, passed
56
+ * on to the index rather than defaulted away.
57
+ * @param {Map<string, object[]>} [opts.sqlTables] - Prepared `sql` results, by
58
+ * note path.
59
+ * @param {object[]} [opts.records] - Index records the caller already derived,
60
+ * so a command that also needs them — every one of them does, to answer its
61
+ * `sql` tables — enumerates the corpus once rather than twice.
62
+ * @param {object[]} [opts.problems] - Collects the notes the index cannot
63
+ * record, as diagnostics, instead of letting one of them abort the check
64
+ * before it has reported anything else.
23
65
  * @returns {object} The notes, the index, and the resolvers built over it.
24
66
  */
25
- export function buildLinkIndex(contentBase: string, { manifestDir, skipDirectories }?: {
26
- manifestDir?: string | undefined;
67
+ export function buildLinkIndex(contentBase: string, { config, skipDirectories, sqlTables, records, problems }?: {
68
+ config?: object | undefined;
27
69
  skipDirectories?: readonly string[] | undefined;
70
+ sqlTables?: Map<string, object[]> | undefined;
71
+ records?: object[] | undefined;
72
+ problems?: object[] | undefined;
28
73
  }): object;
29
74
  /**
30
75
  * Every defect in the addresses a package homepage carries.
@@ -46,7 +91,7 @@ export function buildLinkIndex(contentBase: string, { manifestDir, skipDirectori
46
91
  * and what replaced it, so this is a fact rather than a guess — and it is
47
92
  * exactly the SoHL defect.
48
93
  * - A **hardcoded absolute URL** into this package's own prefix, or into one a
49
- * vendored manifest names. Every one of them has a better form to write, which
94
+ * a fetched index names. Every one of them has a better form to write, which
50
95
  * is why every one is reported — including a bare `/<package>/`, which names
51
96
  * another package's landing (#87).
52
97
  *
@@ -18,13 +18,22 @@ export function isValidShortcode(value: unknown): boolean;
18
18
  * ignores. Defaults to the configured list.
19
19
  * @param {string} [opts.contentPackage] - The package this tree builds, for the
20
20
  * homepage rule. Dropped from that finding when unknown rather than guessed.
21
+ * @param {object} [opts.config] - The resolved build configuration, which the
22
+ * corpus is derived against.
23
+ * @param {readonly object[]} [opts.records] - Index records the caller already
24
+ * derived, so a command reads one corpus (#243).
25
+ * @param {object[]} [opts.problems] - Collects the notes the index cannot
26
+ * record, instead of letting one of them silence the lint.
21
27
  * @returns {{findings: Array<{file: string, line?: number, column?: number,
22
28
  * severity: "error"|"warning", message: string}>, notes: number,
23
29
  * keys: number}} The findings, and what was inspected to produce them.
24
30
  */
25
- export function lintContentTree(contentBase: string, { skipDirectories, contentPackage }?: {
31
+ export function lintContentTree(contentBase: string, { skipDirectories, contentPackage, config, records, problems }?: {
26
32
  skipDirectories?: readonly string[] | undefined;
27
33
  contentPackage?: string | undefined;
34
+ config?: object | undefined;
35
+ records?: readonly object[] | undefined;
36
+ problems?: object[] | undefined;
28
37
  }): {
29
38
  findings: Array<{
30
39
  file: string;
@@ -2,7 +2,8 @@
2
2
  * The **content** package: the distribution unit this repository's notes belong
3
3
  * to, and the **address namespace** every one of them is published under.
4
4
  *
5
- * It is the first segment of every canonical key (`sohl-skill-clmb`), the name
5
+ * It is the first segment of every canonical key
6
+ * (`package-system-type-shortcode`, so `sohl-none-doc-gear`), the name
6
7
  * of the link manifest this build emits (`sohl.json`), and the package a
7
8
  * cross-package wikilink writes to reach one of these notes. So it is the
8
9
  * repository's identity in the address space, not a switch — and never dead
@@ -73,52 +73,34 @@ export function selectRows(spec: object, docs: Array<ContentTableDoc>, self?: Co
73
73
  * @returns {string} The markdown table (no trailing newline).
74
74
  */
75
75
  export function renderContentTable(spec: object, rows: Array<ContentTableDoc>, linkable: (doc: ContentTableDoc) => boolean, self?: ContentTableDoc): string;
76
- /**
77
- * Expand every fenced `dataview` block in a markdown body.
78
- *
79
- * A block that cannot be honoured — malformed or unsupported — is left in the
80
- * body verbatim and reported in `errors`, so the failure is visible in the
81
- * output as well as on the console. Every other code fence, and every code
82
- * span, is left alone (that is how the syntax is documented).
83
- *
84
- * A query that matches **no** note is not an error: it renders as an empty
85
- * table (headers only), which is what the author already sees in Obsidian, and
86
- * a category with no content yet is a normal state of the corpus rather than a
87
- * broken build.
88
- *
89
- * @param {string} markdown - The note body, frontmatter already stripped.
90
- * @param {object} ctx
91
- * @param {Array<ContentTableDoc>} ctx.docs - The searchable universe: every
92
- * content note the caller considers in scope.
93
- * @param {(doc: ContentTableDoc) => boolean} [ctx.linkable] - Whether a note can
94
- * be linked to from a cell; defaults to never.
95
- * @param {string} [ctx.source] - The note being expanded, for error reports.
96
- * @param {ContentTableDoc} [ctx.self] - The note being expanded, as a searchable
97
- * doc: what a query's `this` reads.
98
- * @returns {{markdown: string, errors: Array<{source: string, directive: string,
99
- * reason: string, line: number}>, lineMap: Array<{line: number,
100
- * generated: boolean}>}} `lineMap` is parallel to the emitted lines and says
101
- * which authored line each came from, so a diagnostic about the expanded
102
- * body can name an authored position (#17). An `errors` entry carries the
103
- * 0-based line of the directive that failed, for the same reason.
104
- */
105
- export function expandContentTables(markdown: string, { docs, linkable, source, self }?: {
106
- docs: Array<ContentTableDoc>;
107
- linkable?: ((doc: ContentTableDoc) => boolean) | undefined;
76
+ export function expandContentTables(markdown: any, { docs, linkable, source, self, sqlTables, }?: {
77
+ docs?: never[] | undefined;
78
+ linkable?: (() => boolean) | undefined;
108
79
  source?: string | undefined;
109
- self?: ContentTableDoc | undefined;
80
+ self?: undefined;
81
+ sqlTables?: undefined;
110
82
  }): {
111
83
  markdown: string;
112
- errors: Array<{
84
+ errors: ({
113
85
  source: string;
114
86
  directive: string;
115
- reason: string;
87
+ reason: any;
116
88
  line: number;
117
- }>;
118
- lineMap: Array<{
89
+ column: number;
90
+ } | {
91
+ source: string;
92
+ directive: string;
93
+ reason: any;
119
94
  line: number;
120
- generated: boolean;
121
- }>;
95
+ column?: undefined;
96
+ })[];
97
+ warnings: {
98
+ source: string;
99
+ line: number;
100
+ column: number;
101
+ reason: string;
102
+ }[];
103
+ lineMap: any[];
122
104
  };
123
105
  /**
124
106
  * One content note as a content build hands it to the expander: its parsed
@@ -53,6 +53,13 @@ export function defineDocumentSubtypes({ system, block, types }?: {
53
53
  /**
54
54
  * The row a system declares for a note type, or nothing.
55
55
  *
56
+ * The **one** place a map is indexed, so the retirement window for a renamed
57
+ * type is honoured everywhere a row is asked for — {@link documentSubtype},
58
+ * {@link mapsNoteType}, {@link noteTypesFor}'s consumers and
59
+ * {@link systemOf} all arrive here. A note still spelling `armorgear` finds the
60
+ * `armor` row, compiles into the `armorgear` document it always did, and keeps
61
+ * the address it publishes at; only the *report* tells it to move (#78).
62
+ *
56
63
  * @param {DocumentSubtypeMap} map - The system's map.
57
64
  * @param {string|undefined} noteType - The note's declared `type`.
58
65
  * @returns {Readonly<DocumentSubtypeRow>|undefined} The row, or `undefined`
@@ -152,9 +159,17 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
152
159
  * whole point of the issue.
153
160
  *
154
161
  * A **retired** spelling is refused by name before any of that. Without it a
155
- * reference left behind by a rename would take the unmapped fallback and
156
- * address a document of the old name — resolving silently, which is precisely
157
- * what a retirement exists to stop (#78).
162
+ * reference left behind by a merge would take the unmapped fallback and address
163
+ * a document of the old name — resolving silently, which is precisely what a
164
+ * retirement exists to stop.
165
+ *
166
+ * A **renamed** spelling is the opposite case and resolves normally, through
167
+ * the same {@link currentType} every other lookup goes through (#78). It has to
168
+ * be this side as much as the note's own `type:`: the overwhelming majority of
169
+ * the 31,000 occurrences of the old names are `(type, shortcode)` references
170
+ * inside a being's `items:` list, so a window that resolved notes but not
171
+ * references would silently drop 30,000 embedded items rather than compile them
172
+ * unchanged.
158
173
  *
159
174
  * @param {DocumentSubtypeMap} map - The system's map.
160
175
  * @param {string|undefined} noteType - The type the reference names.
@@ -163,6 +178,24 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
163
178
  * @returns {ReferencedSubtype} The subtype, or why there is none.
164
179
  */
165
180
  export function referencedSubtype(map: DocumentSubtypeMap, noteType: string | undefined, document: string): ReferencedSubtype;
181
+ /**
182
+ * Which system defines the document a note of this type compiles into.
183
+ *
184
+ * A journal, a macro, a scene and an item's documentation journal belong to no
185
+ * system and answer {@link NO_SYSTEM}; an item or an actor belongs to whichever
186
+ * system's map claims its note type.
187
+ *
188
+ * `maps` is required rather than defaulted, so this stays reachable from any
189
+ * pass: the registry of maps lives in `note-claims.mjs`, which imports half the
190
+ * engine, and defaulting to it here would drag that in wherever an address is
191
+ * built.
192
+ *
193
+ * @param {string} type - The note's `type`.
194
+ * @param {readonly object[]} maps - The document-subtype maps this build ships.
195
+ * @returns {string} The system id, or {@link NO_SYSTEM}.
196
+ */
197
+ export function systemOf(type: string, maps: readonly object[]): string;
198
+ export { NO_SYSTEM };
166
199
  /**
167
200
  * One row of a system's map: what a note of this type becomes.
168
201
  */
@@ -231,3 +264,4 @@ export type ReferencedSubtype = {
231
264
  */
232
265
  problem?: string | undefined;
233
266
  };
267
+ import { NO_SYSTEM } from "./systems.mjs";
@@ -1,3 +1,22 @@
1
+ /**
2
+ * Whether a resolution read a field from the position it is being swept off.
3
+ *
4
+ * The sweep's progress signal, in one predicate so the compile-time report and
5
+ * the frontmatter lint cannot disagree about what counts — the role
6
+ * {@link module:engine/retired-fields.declaresRetiredAlias} plays for a renamed
7
+ * field.
8
+ *
9
+ * **Only for a field that declares a `legacyKey`.** Every other field's
10
+ * in-block position is simply where it lives; reporting those would put a
11
+ * finding on every field of every note in every tree, which is #126's corpus
12
+ * migration rather than a signal anyone could act on.
13
+ *
14
+ * @param {FieldSpec} field - The declaration.
15
+ * @param {import("./system-block.mjs").FieldSource} from - Where
16
+ * {@link resolveFieldValue} said the value came from.
17
+ * @returns {boolean} True when the value came from the retiring position.
18
+ */
19
+ export function readsLegacyKey(field: FieldSpec, from: import("./system-block.mjs").FieldSource): boolean;
1
20
  /**
2
21
  * Read one declared field out of a note's frontmatter.
3
22
  *
@@ -14,10 +33,16 @@
14
33
  * @param {string} [options.block="sohl"] - Which system's block to resolve
15
34
  * against. The default is the one block every existing tree authors; a
16
35
  * second system passes its own.
36
+ * @param {(field: FieldSpec) => void} [options.onLegacyKey] - Called with each
37
+ * field read from the position it is being swept off (#305). A callback
38
+ * rather than a returned list because the caller is a compiler, which already
39
+ * knows the note and how to locate a key in it; this module knows neither and
40
+ * would have to invent a finding shape to say so.
17
41
  * @returns {any} The value to emit.
18
42
  */
19
- export function readField(field: FieldSpec, fm: object, { block }?: {
43
+ export function readField(field: FieldSpec, fm: object, { block, onLegacyKey }?: {
20
44
  block?: string | undefined;
45
+ onLegacyKey?: ((field: FieldSpec) => void) | undefined;
21
46
  }): any;
22
47
  /**
23
48
  * Turn a field declaration into the builder it declares.
@@ -27,10 +52,14 @@ export function readField(field: FieldSpec, fm: object, { block }?: {
27
52
  * @param {string} [options.block="sohl"] - Which system's block the builder
28
53
  * reads. One declaration compiles against any block, which is what lets two
29
54
  * systems declare the same shared source and different destinations.
55
+ * @param {(field: FieldSpec) => void} [options.onLegacyKey] - Passed through to
56
+ * {@link readField}: called with each field the note authored at the position
57
+ * it is being swept off (#305).
30
58
  * @returns {(fm: object) => object} A `system`-block builder.
31
59
  */
32
- export function buildFromFields(fields: readonly FieldSpec[], { block }?: {
60
+ export function buildFromFields(fields: readonly FieldSpec[], { block, onLegacyKey }?: {
33
61
  block?: string | undefined;
62
+ onLegacyKey?: ((field: FieldSpec) => void) | undefined;
34
63
  }): (fm: object) => object;
35
64
  /**
36
65
  * The fields of a declaration an author actually writes.
@@ -44,7 +73,6 @@ export function buildFromFields(fields: readonly FieldSpec[], { block }?: {
44
73
  * @returns {FieldSpec[]} Only the fields with a frontmatter `name`.
45
74
  */
46
75
  export function authoredFields(fields: readonly FieldSpec[]): FieldSpec[];
47
- export { setPath };
48
76
  /**
49
77
  * @typedef {object} FieldSpec
50
78
  * @property {string} to - Dotted path in the emitted `system` block — and,
@@ -60,9 +88,28 @@ export { setPath };
60
88
  * to share a name. They constantly do not — one shared `data.portrait` feeds
61
89
  * `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
62
90
  * declared rather than matched by spelling (#58). The in-block position is
63
- * still read, second, until #126 moves the corpus off it.
91
+ * still read, second, until #126 moves the corpus off it — keyed on
92
+ * `legacyKey` where the two spellings differ.
64
93
  *
65
94
  * Absent means the value is not authored at all — see `value`.
95
+ * @property {string} [legacyKey] - **The key this field is authored at inside
96
+ * the system block** — the second position of the resolution order — when
97
+ * that is not `name`. Absent, the position is keyed on `name`.
98
+ *
99
+ * The two were one property until #305, which held only while a field's
100
+ * shared source and its in-block key were the same word. `data:` (#128) ended
101
+ * that: a shared source is a path into a container, so `data.species` and
102
+ * `species` name two different places and no single value reached both.
103
+ * `name: "species"` could not see `data.species`; `name: "data.species"`
104
+ * could not see `hm3.species`; and each yielded the field's **default**
105
+ * wherever only the other position was authored, with the note compiling and
106
+ * the value simply gone.
107
+ *
108
+ * Declaring both restores the shape every other retirement in this package
109
+ * uses — read both spellings, let the current one win, and *report* the
110
+ * retiring one — so a field can move into `data:` while the corpus catches
111
+ * up, instead of on a flag day across four repositories. A declaration that
112
+ * names one is mid-sweep by construction; see {@link readsLegacyKey}.
66
113
  * @property {string} [topLevelMeans] - **What the note's top-level key of this
67
114
  * name means instead** — declared only where it means something else, and
68
115
  * stating it removes the shared top-level position from this field's
@@ -190,11 +237,33 @@ export type FieldSpec = {
190
237
  * to share a name. They constantly do not — one shared `data.portrait` feeds
191
238
  * `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
192
239
  * declared rather than matched by spelling (#58). The in-block position is
193
- * still read, second, until #126 moves the corpus off it.
240
+ * still read, second, until #126 moves the corpus off it — keyed on
241
+ * `legacyKey` where the two spellings differ.
194
242
  *
195
243
  * Absent means the value is not authored at all — see `value`.
196
244
  */
197
245
  name?: string | undefined;
246
+ /**
247
+ * - **The key this field is authored at inside
248
+ * the system block** — the second position of the resolution order — when
249
+ * that is not `name`. Absent, the position is keyed on `name`.
250
+ *
251
+ * The two were one property until #305, which held only while a field's
252
+ * shared source and its in-block key were the same word. `data:` (#128) ended
253
+ * that: a shared source is a path into a container, so `data.species` and
254
+ * `species` name two different places and no single value reached both.
255
+ * `name: "species"` could not see `data.species`; `name: "data.species"`
256
+ * could not see `hm3.species`; and each yielded the field's **default**
257
+ * wherever only the other position was authored, with the note compiling and
258
+ * the value simply gone.
259
+ *
260
+ * Declaring both restores the shape every other retirement in this package
261
+ * uses — read both spellings, let the current one win, and *report* the
262
+ * retiring one — so a field can move into `data:` while the corpus catches
263
+ * up, instead of on a flag day across four repositories. A declaration that
264
+ * names one is mid-sweep by construction; see {@link readsLegacyKey}.
265
+ */
266
+ legacyKey?: string | undefined;
198
267
  /**
199
268
  * - **What the note's top-level key of this
200
269
  * name means instead** — declared only where it means something else, and
@@ -284,4 +353,6 @@ export type FieldSpec = {
284
353
  */
285
354
  describe: string;
286
355
  };
356
+ import { legacyKeyOf } from "./system-block.mjs";
287
357
  import { setPath } from "./system-block.mjs";
358
+ export { legacyKeyOf, setPath };