@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
@@ -45,10 +45,11 @@
45
45
 
46
46
  import log from "loglevel";
47
47
 
48
- import { sohlField, makeId, resolveName, defaultStats, md } from "./helpers.mjs";
48
+ import { sohlField, makeId, resolveName, defaultStats, md, folderField } from "./helpers.mjs";
49
49
  import { BasePackCompiler } from "./base-compiler.mjs";
50
50
  import { anchorPageId } from "./wikilinks.mjs";
51
51
  import { hasDocEntry, itemDocEntryId } from "./item-docs.mjs";
52
+ import { JOURNAL_TYPES } from "./ids.mjs";
52
53
 
53
54
  /**
54
55
  * Splits a markdown body into pages by top-level H1 headings. Fenced
@@ -129,45 +130,91 @@ export function splitPages(body, leadName = "Introduction") {
129
130
  }
130
131
 
131
132
  /**
132
- * Two headings in one note sharing an `{#anchor}` derive the same page id, which
133
- * the LevelDB packer reports only as an opaque duplicate-key collision. Catch it
134
- * here, where the note and the slug can be named.
133
+ * Two pages in one note that would derive the same id, which the LevelDB packer
134
+ * reports only as an opaque duplicate-key collision. Catch it here, where the
135
+ * note and the page can be named.
135
136
  *
136
- * @param {Array<{anchorSlug: string|null}>} rawPages - From {@link splitPages}.
137
+ * Both halves of {@link journalPageId} are checked, because each is now keyed
138
+ * on an identity alone:
139
+ *
140
+ * - **An anchor**, declared twice, has always collided.
141
+ * - **A name**, repeated among the unanchored pages, collides since #268 took
142
+ * the index out of the key. `MD024` with `siblings_only` already makes two
143
+ * sibling headings with the same text a lint error, so this is the same rule
144
+ * restated where the build can enforce it — a lint is a separate command, and
145
+ * the compile must not depend on someone having run it.
146
+ *
147
+ * The two are counted separately: an anchored page takes its id from the slug
148
+ * and an unanchored one from the name, so a page named for another's anchor is
149
+ * not a collision.
150
+ *
151
+ * @param {Array<{anchorSlug: string|null, name: string}>} rawPages - From
152
+ * {@link splitPages}.
137
153
  * @param {string} noteName - The note, for the error message.
138
- * @throws {Error} When an anchor is declared twice in the same note.
154
+ * @throws {Error} When two pages in one note share an anchor or a name.
139
155
  */
140
- export function assertUniqueAnchors(rawPages, noteName) {
141
- const seen = new Set();
156
+ export function assertUniquePages(rawPages, noteName) {
157
+ const anchors = new Set();
158
+ const names = new Set();
142
159
  for (const page of rawPages) {
143
- if (!page.anchorSlug) continue;
144
- if (seen.has(page.anchorSlug)) {
160
+ if (page.anchorSlug) {
161
+ if (anchors.has(page.anchorSlug)) {
162
+ throw new Error(
163
+ `note "${noteName}" declares the anchor {#${page.anchorSlug}} on more than one heading; an anchor must be unique within its note`,
164
+ );
165
+ }
166
+ anchors.add(page.anchorSlug);
167
+ continue;
168
+ }
169
+ if (names.has(page.name)) {
145
170
  throw new Error(
146
- `note "${noteName}" declares the anchor {#${page.anchorSlug}} on more than one heading; an anchor must be unique within its note`,
171
+ `note "${noteName}" has more than one page named "${page.name}"; a page is identified by its heading, so the two would compile to one document — rename one, or give it an {#anchor}`,
147
172
  );
148
173
  }
149
- seen.add(page.anchorSlug);
174
+ names.add(page.name);
150
175
  }
151
176
  }
152
177
 
178
+ /**
179
+ * The anchor half of {@link assertUniquePages}, under its former name.
180
+ *
181
+ * @deprecated Call {@link assertUniquePages}, which checks page names too.
182
+ * @param {Array<{anchorSlug: string|null, name: string}>} rawPages - From
183
+ * {@link splitPages}.
184
+ * @param {string} noteName - The note, for the error message.
185
+ */
186
+ export function assertUniqueAnchors(rawPages, noteName) {
187
+ assertUniquePages(rawPages, noteName);
188
+ }
189
+
153
190
  /**
154
191
  * The id of one page within its entry.
155
192
  *
156
193
  * An anchored page takes the id its inbound links compute from the note id and
157
194
  * the slug, so link and page agree without shared state. Every other page is
158
- * keyed by its position and name, which is what lets the items pass address an
159
- * item doc's first page without having compiled it (see
195
+ * keyed by its **name**, which is what lets the items pass address an item
196
+ * doc's first page without having compiled it (see
160
197
  * {@link sohl.utils.packs.itemDocPointer}).
161
198
  *
199
+ * **It takes no index** (#268). A page used to be keyed by position *and* name,
200
+ * so inserting a heading renumbered every page after it and a re-import created
201
+ * new pages beside the old ones — while nothing about those pages had changed.
202
+ * The anchored case above never took one, and is the shape this now shares.
203
+ *
204
+ * The name is a sound identity here in a way it is not for an embedded item: a
205
+ * page's name is its heading, and `MD024` with `siblings_only` is in the shared
206
+ * markdownlint rule set, so two sibling headings with the same text are already
207
+ * a lint error. {@link assertUniquePages} states the same thing at compile
208
+ * time, where the packer would otherwise report only an opaque duplicate key.
209
+ *
162
210
  * @param {string} entryId - The owning JournalEntry's `_id`.
163
211
  * @param {{anchorSlug: string|null, name: string}} page - From {@link splitPages}.
164
- * @param {number} index - The page's position in the entry.
165
212
  * @returns {string} A 16-character Foundry id.
166
213
  */
167
- export function journalPageId(entryId, page, index) {
214
+ export function journalPageId(entryId, page) {
168
215
  return page.anchorSlug ?
169
216
  anchorPageId(entryId, page.anchorSlug)
170
- : makeId("journal-page", `${entryId}:${index}:${page.name}`);
217
+ : makeId("journal-page", `${entryId}:${page.name}`);
171
218
  }
172
219
 
173
220
  /**
@@ -188,9 +235,9 @@ export function buildPages(rawPages, entryId, noteName) {
188
235
  `note "${noteName}" has no Introduction content and no H1 headings — nothing to compile`,
189
236
  );
190
237
  }
191
- assertUniqueAnchors(rawPages, noteName);
192
- return rawPages.map((page, index) => {
193
- const pageId = journalPageId(entryId, page, index);
238
+ assertUniquePages(rawPages, noteName);
239
+ return rawPages.map((page) => {
240
+ const pageId = journalPageId(entryId, page);
194
241
  return {
195
242
  _id: pageId,
196
243
  name: page.name,
@@ -278,15 +325,21 @@ export class Journals extends BasePackCompiler {
278
325
  * (#1348); a macro's is the same arrangement (#1514), and so is a map's,
279
326
  * whose prose is the place description its pins point at (#1525).
280
327
  *
281
- * The membership is {@link sohl.utils.packs.docEntryTypes}, read through
282
- * {@link sohl.utils.packs.hasDocEntry} the one set the link manifest also
283
- * reads, so what compiles and what is published cannot drift apart.
328
+ * Two memberships, and they mean different things.
329
+ * {@link module:engine/ids.JOURNAL_TYPES} is the types whose whole document
330
+ * *is* a journal `doc`, and since #241 `place`, `lore` and `scenario`,
331
+ * which the content format has always described and nothing compiled.
332
+ * {@link sohl.utils.packs.docEntryTypes}, read through
333
+ * {@link sohl.utils.packs.hasDocEntry}, is the types whose prose becomes a
334
+ * journal *beside* another document. The second is the one the link
335
+ * manifest also reads, so what compiles and what is published cannot drift
336
+ * apart; the first has no second document to point at.
284
337
  *
285
338
  * @param {object} fm - The note's frontmatter.
286
- * @returns {boolean} True for a `doc` note or a doc-carrying note.
339
+ * @returns {boolean} True for a journal-only note or a doc-carrying one.
287
340
  */
288
341
  selects(fm) {
289
- return fm.type === "doc" || hasDocEntry(fm.type);
342
+ return JOURNAL_TYPES.has(String(fm.type)) || hasDocEntry(fm.type);
290
343
  }
291
344
 
292
345
  /**
@@ -333,8 +386,21 @@ export class Journals extends BasePackCompiler {
333
386
  // against this pack's own folders.yaml — an item folder is declared in
334
387
  // the items one, a macro folder in the macros one, and a map's in the
335
388
  // scenes one.
336
- const folderId = sohlField(fm, "folder", null);
337
- const folder = ownsDoc ? folderId : this.folderResolver(folderId);
389
+ const { value: authoredFolder, isAddress } = folderField(fm);
390
+ // An address is resolved wherever it is written, including here — and
391
+ // resolving it *here* is what cures the defect this comment used to
392
+ // describe. A folder note has one definition and one address, so the
393
+ // journals pack materialises the very folder the items pack does, by
394
+ // the same id (#257). There is no second folder file left to disagree
395
+ // with the first, and so no arrangement to assume: the mirroring
396
+ // failure is unrepresentable rather than merely reported.
397
+ //
398
+ // `folder:` is unchanged, and still crosses packs verbatim on the
399
+ // assumption both declare it — the arrangement #260 retires.
400
+ const folder =
401
+ isAddress ? this.folderResolver(authoredFolder, { isAddress: true })
402
+ : ownsDoc ? authoredFolder
403
+ : this.folderResolver(authoredFolder);
338
404
 
339
405
  return buildJournalEntry({
340
406
  id,
package/engine/macros.mjs CHANGED
@@ -51,7 +51,7 @@
51
51
 
52
52
  import log from "loglevel";
53
53
 
54
- import { sohlField, resolveName, resolveImg, defaultStats } from "./helpers.mjs";
54
+ import { sohlField, resolveName, resolveImg, defaultStats, folderField } from "./helpers.mjs";
55
55
  import { BasePackCompiler } from "./base-compiler.mjs";
56
56
  import { splitPages } from "./journals.mjs";
57
57
 
@@ -315,7 +315,9 @@ export class Macros extends BasePackCompiler {
315
315
  const name = resolveName(fm);
316
316
  return buildMacroEntry(fm, {
317
317
  command: macroCommand(body, name),
318
- folder: this.folderResolver(sohlField(fm, "folder", null)),
318
+ folder: this.folderResolver(folderField(fm).value, {
319
+ isAddress: folderField(fm).isAddress,
320
+ }),
319
321
  // This pack's system, not the package-wide one (#48).
320
322
  stats: this.stats,
321
323
  });