@heroiclands/package-build 22.1.0 → 22.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 22.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 241451e: **`schema.json` is a release asset, not a committed file.** `package-build schema` writes `build/schema.json` instead of the repository root, and `--check` is gone — there is no committed copy left to compare against. `package-build release` publishes `schema.json` beside the archive and the manifest, the way it already publishes the content index, whenever the staged tree carries one.
8
+
9
+ To keep publishing a schema: drop the committed `schema.json` and any lint step that runs `package-build schema --check`; add `{ from: build/schema.json, to: schema.json }` to `packageBuild.assets`, and run `package-build schema` before `package-build assets` in the build chain so the release never ships one older than the source it was cut from.
10
+ - 483b630: **`content-build site` refuses to generate a configuration with no `site.assets`.** The theme resolves every relative asset — the brand logo, the 404 hero, every CDN-resolved image — against `site.assets`, and there is no defensible default: a package that built a site with the key absent published every one of those as a broken relative path. Declare `site.assets` — the `https://` host every package's imagery is served from — in `package-build.config.yaml` for any package that publishes a site.
11
+ - 985f599: **A package now describes itself once for Foundry and once for the site.** Foundry's package browser wants a pitch — HTML, any length — and a site's `<meta name="description">` wants one plain sentence; deriving both from `package.json`'s `description` forced one string onto both. Declare `packageBuild.manifest.descriptionHtml` in `package-build.config.yaml` for the Foundry pitch (HTML allowed, emitted as the manifest's `description`) and `site.description` for the site's meta description (plain text, required for `content-build site`). `package.json`'s own `description` is read by neither any more — a warning names both keys when one is still declared — and the field can be deleted.
12
+
13
+ ## 22.1.1
14
+
15
+ ### Patch Changes
16
+
17
+ - f02b25a: **A relationship may declare `contentIndex: false`.** A `requires` or
18
+ `systems` entry naming a package Foundry installs but the content tree never
19
+ cites by wikilink no longer needs a fetched index just to satisfy
20
+ `deps fetch`. `contentIndex` (default `true`) narrows a relationship declaring
21
+ it `false` to the Foundry manifest only: `deps fetch` fetches nothing for it,
22
+ and a wikilink into it fails at the link, naming the key, rather than
23
+ resolving against a stale declaration or an index nobody fetched. It cannot be
24
+ combined with `itemCatalog: true`, which extracts items from the same index
25
+ this declares there is none of.
26
+ - 07bf15d: **The generated Hugo configuration emits tag pages for a site whose notes carry `tags:`.** `content-build site` reads whether any note in the tree carries `tags:` and, when at least one does, writes `[taxonomies] tag = "tags"` and `[outputs] taxonomy = ["HTML"], term = ["HTML"]` into `build/hugo/hugo.toml`, and leaves `taxonomy` and `term` enabled among `disableKinds`. A site whose notes carry no `tags:` gets the same configuration as before — `taxonomy`, `term` and `RSS` all disabled, no `[taxonomies]` or `[outputs]` block. `site.hugo.disableKinds`, `.taxonomies` and `.outputs` stay refused under `site.hugo`, each naming this derivation as the source.
27
+
3
28
  ## 22.1.0
4
29
 
5
30
  ### Minor Changes
package/CONTENT.md CHANGED
@@ -1782,6 +1782,28 @@ asked for — a build that downloads silently is not reproducible and fails
1782
1782
  strangely offline. The cache is keyed by version, so changing the pinned version
1783
1783
  is a miss rather than a silent overwrite.
1784
1784
 
1785
+ ### A relationship may be a Foundry dependency only
1786
+
1787
+ `requires` and `systems` install with Foundry whether or not the tree cites
1788
+ them, and a package may need the one without the other — thalornaaltart
1789
+ `requires` Thalorna so Foundry installs the base module, and its one homepage
1790
+ note links nowhere. Declare `contentIndex: false` on that entry to say so:
1791
+
1792
+ ```yaml
1793
+ relationships:
1794
+ requires:
1795
+ - id: thalorna
1796
+ type: module
1797
+ manifest: https://github.com/HeroicLands/thalorna/releases/latest/download/module.json
1798
+ contentIndex: false
1799
+ ```
1800
+
1801
+ `deps fetch` fetches nothing for it — no cache directory, nothing to go stale —
1802
+ and a wikilink into it fails at the link, naming `contentIndex`, rather than
1803
+ resolving against a stale declaration or an index nobody fetched. It cannot be
1804
+ combined with `itemCatalog: true`, which extracts items from the same index
1805
+ this declares there is none of.
1806
+
1785
1807
  ### `packagebuild` needs no declaration
1786
1808
 
1787
1809
  package-build ships a set of images of its own — section banners chiefly — and a
@@ -1492,7 +1492,10 @@ function pdfCommand() {
1492
1492
  * **The Hugo configuration is generated before anything is written.** Its
1493
1493
  * sources — `package.json`'s `homepage`, the cached navigation, the installed
1494
1494
  * theme — are each a way the build can fail, and failing before the output
1495
- * tree is cleared leaves the last good site in place to be looked at.
1495
+ * tree is cleared leaves the last good site in place to be looked at. It is
1496
+ * generated again once the site walk completes, because whether the site
1497
+ * emits taxonomy pages is read from the walk — whether any note carries
1498
+ * `tags:` — and that is not known until then.
1496
1499
  *
1497
1500
  * **Each gate is reported and the run stops at the first that fires.** They are
1498
1501
  * ordered so the report names the cause rather than its symptoms: an unusable
@@ -1510,7 +1513,14 @@ function siteCommand() {
1510
1513
  handler: async () => {
1511
1514
  try {
1512
1515
  const config = loadPackConfig();
1513
- const hugo = generateHugoConfig(config);
1516
+ // Generated once before the walk, purely to fail fast on a
1517
+ // missing or mismatched source — `homepage`, the manifest
1518
+ // title, the cached navigation, the installed theme — while
1519
+ // the last good site is still in place to be looked at. Its
1520
+ // `disableKinds`/`taxonomies`/`outputs` are provisional: only
1521
+ // the site walk below knows whether any note carries `tags:`,
1522
+ // so the value actually written is regenerated after it runs.
1523
+ generateHugoConfig(config);
1514
1524
  const result = buildSite({
1515
1525
  config,
1516
1526
  sqlTables: await prepareTreeSqlTables(config.paths.content, {
@@ -1628,6 +1638,7 @@ function siteCommand() {
1628
1638
  `${s.tree ?? 0} tree page(s) + ${s.landings} ` +
1629
1639
  `landing(s) to ${path.relative(process.cwd(), s.out)}`,
1630
1640
  );
1641
+ const hugo = generateHugoConfig(config, { hasTags: result.hasTags });
1631
1642
  const { file } = writeHugoConfig(config, hugo);
1632
1643
  log.info(`wrote ${path.relative(process.cwd(), file)}`);
1633
1644
  } catch (err) {
@@ -1860,7 +1871,22 @@ function depsCommand() {
1860
1871
  if (indexes) log.info(`Fetched ${indexes} dependency content index(es).`);
1861
1872
  const count = await fetchAllCatalogs(config);
1862
1873
  if (count) log.info(`Fetched ${count} dependency catalogue(s).`);
1863
- if (!indexes && !count) log.info("This package declares no dependencies.");
1874
+ if (!indexes && !count) {
1875
+ // Distinguished from a package declaring nothing at all: a
1876
+ // relationship may still be declared, just narrowed to the
1877
+ // Foundry manifest by `contentIndex: false` — reporting
1878
+ // "no dependencies" there would read as though the
1879
+ // declaration itself had gone missing.
1880
+ const declared = Object.values(config.relationships ?? {}).some(
1881
+ (entries) => entries?.length,
1882
+ );
1883
+ log.info(
1884
+ declared ?
1885
+ "No declared dependency needs a fetched content index or item " +
1886
+ "catalogue."
1887
+ : "This package declares no dependencies.",
1888
+ );
1889
+ }
1864
1890
  } catch (err) {
1865
1891
  reportFailure(err);
1866
1892
  process.exitCode = 1;
@@ -334,9 +334,11 @@ async function formatGenerated(text, filepath) {
334
334
  * so the system publishes the field sets as data — the same shape the link
335
335
  * manifest already uses for addresses.
336
336
  *
337
- * `--check` fails when the committed copy disagrees with what the source would
338
- * produce now, because a generated file nothing checks drifts from its
339
- * generator silently and this one is read by other repositories.
337
+ * Writes under `build/`, alongside every other generated artifact: a release
338
+ * publishes it beside the archive and the manifest (see `package-build
339
+ * release`), and `packageBuild.assets` carries it into the staged tree from
340
+ * there. There is nothing committed to compare it against, so there is no
341
+ * `--check`.
340
342
  *
341
343
  * @returns {object} The yargs command module.
342
344
  */
@@ -344,15 +346,8 @@ function schemaCommand() {
344
346
  return {
345
347
  command: "schema",
346
348
  describe: "Publish this package's DataModel field sets as schema.json",
347
- builder: (y) =>
348
- y.option("check", {
349
- type: "boolean",
350
- default: false,
351
- describe:
352
- "Fail when the committed schema.json is out of date " +
353
- "rather than rewriting it",
354
- }),
355
- handler: handler(async (argv) => {
349
+ builder: (y) => y,
350
+ handler: handler(async () => {
356
351
  const config = loadPackageBuildConfig();
357
352
  if (!config.schema.length) {
358
353
  console.log(
@@ -369,25 +364,13 @@ function schemaCommand() {
369
364
  version: pkg.version,
370
365
  });
371
366
 
372
- const out = path.join(config.rootDir, SCHEMA_ARTIFACT_FILE);
367
+ const out = path.join(config.rootDir, "build", SCHEMA_ARTIFACT_FILE);
373
368
  const text = await formatGenerated(JSON.stringify(artifact), out);
374
369
  const counts = Object.entries(artifact.documents)
375
370
  .map(([kind, subtypes]) => `${Object.keys(subtypes).length} ${kind}`)
376
371
  .join(", ");
377
372
 
378
- if (argv.check) {
379
- const current = fs.existsSync(out) ? fs.readFileSync(out, "utf8") : null;
380
- if (current !== text) {
381
- die(
382
- `${SCHEMA_ARTIFACT_FILE} does not match what this ` +
383
- `package's data models would produce — regenerate ` +
384
- `it with \`package-build schema\`.`,
385
- );
386
- }
387
- console.log(`✅ ${SCHEMA_ARTIFACT_FILE} is up to date ` + `(${counts} subtypes).`);
388
- return;
389
- }
390
-
373
+ fs.mkdirSync(path.dirname(out), { recursive: true });
391
374
  fs.writeFileSync(out, text, "utf8");
392
375
  console.log(
393
376
  `✅ Wrote ${SCHEMA_ARTIFACT_FILE} for ${artifact.system} ` +
package/config.mjs CHANGED
@@ -91,7 +91,7 @@ const SECTION_KEYS = [
91
91
  export const DERIVED_MANIFEST_KEYS = Object.freeze({
92
92
  id: "`foundryPackage`, itself derived from package.json `name`",
93
93
  version: "package.json `version`",
94
- description: "package.json `description`",
94
+ description: "`packageBuild.manifest.descriptionHtml`",
95
95
  url: "package.json `repository`",
96
96
  bugs: "package.json `repository`",
97
97
  manifest: "package.json `repository` and the release tag",
@@ -152,11 +152,16 @@ const ARTIFACT_OF_KIND = Object.freeze({
152
152
  * message, so {@link loadPackageBuildConfig} — the half that knows which file
153
153
  * was read — can resolve it to a line and column. This half stays pure.
154
154
  *
155
+ * Exported so a sibling module composing a configuration value this module
156
+ * does not itself validate — {@link module:engine/site-config}'s
157
+ * `hugoConfig`, checking `site.assets` — reports through the one helper
158
+ * rather than a second copy.
159
+ *
155
160
  * @param {string} where - Dotted path of the offending key.
156
161
  * @param {string} problem - What is wrong with it.
157
162
  * @returns {never}
158
163
  */
159
- function fail(where, problem) {
164
+ export function fail(where, problem) {
160
165
  throw Object.assign(new TypeError(`package-build config: \`${where}\` ${problem}.`), {
161
166
  field: where,
162
167
  });
@@ -503,6 +503,14 @@ export function publishesContentPages(config) {
503
503
  * package this one targets — for a system
504
504
  * relationship, `verified` is what
505
505
  * `_stats.systemVersion` is stamped from.
506
+ * @property {boolean} [contentIndex] Whether `deps fetch` fetches this
507
+ * dependency's content index. Default
508
+ * `true`. `false` declares the dependency
509
+ * for the Foundry manifest only — nothing
510
+ * this tree cites by wikilink — and refuses
511
+ * `itemCatalog: true` on the same entry,
512
+ * since a catalogue is fetched from the same
513
+ * index.
506
514
  */
507
515
 
508
516
  /**
@@ -751,6 +759,7 @@ const DOCS_KEYS = ["itemFields"];
751
759
  const SITE_KEYS = [
752
760
  "base",
753
761
  "assets",
762
+ "description",
754
763
  "packages",
755
764
  "sections",
756
765
  "readmeSections",
@@ -780,6 +789,7 @@ const RELATIONSHIP_KEYS = [
780
789
  "manifest",
781
790
  "compatibility",
782
791
  "itemCatalog",
792
+ "contentIndex",
783
793
  ];
784
794
  const AUTHOR_KEYS = ["name", "email", "url"];
785
795
  const ITEM_BUILDER_KEYS = ["system", "img", "fields"];
@@ -1566,6 +1576,33 @@ function normalizeSiteAssets(value) {
1566
1576
  return assets.replace(/\/+$/, "");
1567
1577
  }
1568
1578
 
1579
+ /**
1580
+ * The site's `<meta name="description">`, plain text.
1581
+ *
1582
+ * Foundry's package browser wants a pitch — HTML, any length,
1583
+ * `packageBuild.manifest.descriptionHtml` — and a `<meta>` tag wants one
1584
+ * plain sentence. The two audiences are different enough that one string
1585
+ * cannot serve both, so this is the site's own, checked for markup rather
1586
+ * than trusted to carry none: a value with a `<` in it is refused, naming
1587
+ * `descriptionHtml` as where markup belongs.
1588
+ *
1589
+ * @param {unknown} value - The configured value, or `undefined`.
1590
+ * @returns {string} The description; `""` when unset.
1591
+ */
1592
+ function normalizeSiteDescription(value) {
1593
+ if (value === undefined) return "";
1594
+ const description = requireNonEmptyString(value, "site.description");
1595
+ if (description.includes("<")) {
1596
+ fail(
1597
+ "site.description",
1598
+ "contains `<` — this is plain text for the site's " +
1599
+ '`<meta name="description">`; markup belongs in ' +
1600
+ "`packageBuild.manifest.descriptionHtml`",
1601
+ );
1602
+ }
1603
+ return description;
1604
+ }
1605
+
1569
1606
  /**
1570
1607
  * A map of section name → landing metadata.
1571
1608
  *
@@ -1604,8 +1641,10 @@ export const DERIVED_HUGO_KEYS = Object.freeze({
1604
1641
  contentDir: "the fixed content mount, `build/hugo/content`",
1605
1642
  themesDir: "where `@heroiclands/hugo-theme` is installed",
1606
1643
  theme: "the installed `@heroiclands/hugo-theme`",
1607
- disableKinds: "the toolchain, which renders the same kinds on every site",
1608
- "params.description": "package.json `description`",
1644
+ disableKinds: "whether any note in the tree carries `tags:`, which the site walk discovers",
1645
+ taxonomies: "whether any note in the tree carries `tags:`, which the site walk discovers",
1646
+ outputs: "whether any note in the tree carries `tags:`, which the site walk discovers",
1647
+ "params.description": "`site.description`",
1609
1648
  "params.author": "package.json `author`",
1610
1649
  "params.cdnBaseURL": "`site.assets`",
1611
1650
  "params.brand": "the organisation's brand links, in `engine/site-config.mjs`",
@@ -1728,6 +1767,7 @@ function normalizeSite(value) {
1728
1767
  const empty = Object.freeze({
1729
1768
  base: "",
1730
1769
  assets: "",
1770
+ description: "",
1731
1771
  packages: Object.freeze([]),
1732
1772
  sections: Object.freeze({}),
1733
1773
  readmeSections: Object.freeze({}),
@@ -1796,6 +1836,7 @@ function normalizeSite(value) {
1796
1836
  return Object.freeze({
1797
1837
  base: input.base === undefined ? "" : requireNonEmptyString(input.base, "site.base"),
1798
1838
  assets: normalizeSiteAssets(input.assets),
1839
+ description: normalizeSiteDescription(input.description),
1799
1840
  packages: Object.freeze(packages),
1800
1841
  sections: normalizeSectionMap(input.sections, "site.sections"),
1801
1842
  readmeSections: normalizeSectionMap(input.readmeSections, "site.readmeSections"),
@@ -2147,6 +2188,24 @@ function normalizeRelationships(value) {
2147
2188
  }
2148
2189
  spec.itemCatalog = rel.itemCatalog;
2149
2190
  }
2191
+ // Opt-out: declares the dependency for the Foundry manifest
2192
+ // only, so `deps fetch` fetches no content index for it and a
2193
+ // wikilink into it is refused rather than silently dead. A
2194
+ // catalogue is fetched from the same index, so it cannot be
2195
+ // declared alongside `itemCatalog: true`.
2196
+ if (rel.contentIndex !== undefined) {
2197
+ if (typeof rel.contentIndex !== "boolean") {
2198
+ fail(`${at}.contentIndex`, "must be true or false");
2199
+ }
2200
+ if (rel.contentIndex === false && spec.itemCatalog) {
2201
+ fail(
2202
+ `${at}.contentIndex`,
2203
+ "cannot be false together with `itemCatalog: true` — a catalogue is " +
2204
+ "fetched from the same index",
2205
+ );
2206
+ }
2207
+ spec.contentIndex = rel.contentIndex;
2208
+ }
2150
2209
  return Object.freeze(spec);
2151
2210
  }),
2152
2211
  );
package/docs/api.md CHANGED
@@ -342,6 +342,7 @@ The closed half of a note's frontmatter: the `data:` container and each type's `
342
342
  | `applicableTagGroups` | `applicableTagGroups(type, groups)` | `object[]` | reading the declared tag groups that apply to a note type |
343
343
  | `exclusiveTagGroups` | `exclusiveTagGroups(type, groups)` | `Array<{slot, tags}>` | reading the single-valued tag slots a note type has, such as a being's kind |
344
344
  | `hasTag` | `hasTag(fm, tag)` | `boolean` | checking whether a note carries a given tag, whatever scalar-or-list form it was authored in |
345
+ | `hasAnyTag` | `hasAnyTag(fm)` | `boolean` | checking whether a note carries any `tags:` at all, for the site build's taxonomy decision |
345
346
  | `isDraftNote` | `isDraftNote(fm)` | `boolean` | checking whether a note is tagged as an unfinished draft |
346
347
  | `subTypeCharsetMessage` | `subTypeCharsetMessage(value)` | `string` | building the message for a `subType` outside the address charset |
347
348
  | `typeCharsetMessage` | `typeCharsetMessage(type)` | `string` | building the message for a `type` outside the address charset |
@@ -491,21 +492,22 @@ The shortcodes a note declares it used to be published under. A package's `(type
491
492
 
492
493
  The published content index — the artifact packages exchange addresses through. **A package publishes its own index; a consumer fetches the ones it depends on.** That is the whole mechanism, and it replaces a vendored link manifest that each repository committed a copy of every other repository's file into. Vendoring failed three ways, and only the last is about staleness:
493
494
 
494
- | Export | Signature | Returns | Use it when |
495
- | ------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
496
- | `METADATA_RELATIONSHIP_KINDS` | `const METADATA_RELATIONSHIP_KINDS` | — | The relationship kinds that are dependencies, and therefore citable. |
497
- | `metadataFileName` | `function metadataFileName(pkg)` | {string} The file name, e.g. | What a package's content index is called, wherever it is written or fetched. |
498
- | `metadataRelationships` | `function metadataRelationships(config)` | {Array<{id: string, manifest: string, kind: string, verified: string\|undefined}>} The dependencies, in declaration order. | Every dependency whose published index this build resolves addresses through. |
499
- | `metadataCacheDir` | `function metadataCacheDir(config, id, version)` | {string} The directory. | The cache directory for one dependency's index at one version. |
500
- | `isComplete` | `const isComplete` | {boolean} True when it was fetched to completion. | Whether a dependency's cache is present and complete. |
501
- | `markComplete` | `function markComplete(dir)` | {void} | Mark a dependency's cache complete. |
502
- | `cachedMetadataFiles` | `function cachedMetadataFiles(config)` | {string[]} One index file per declared dependency. | The fetched index files this build resolves foreign addresses against. |
503
- | `cachedMetadataIndexes` | `function cachedMetadataIndexes(config)` | {Array<{id: string, file: string}>} One entry per declared dependency. | The same fetched indexes, each paired with the package that published it. |
504
- | `newestVersionDir` | `function newestVersionDir(dirs)` | {string} The newest one. | The newest cached version among several version-keyed cache directories. |
505
- | `loadForeignIndexes` | `function loadForeignIndexes(config, localPackages, bases` | {{index: Map<string, object>, packages: Set<string>, stale: Array<{package: string, reason: string}>}} The resolved addresses, which packages contributed, and what could not be read. | Resolve every foreign address this build can cite, from the fetched indexes. |
506
- | `cachedIndexPath` | `function cachedIndexPath(config, pkg)` | {string} A path to name in a diagnostic. | Where a dependency's fetched index sits, for naming it in a diagnostic. |
507
- | `unaddressableForeignPackages` | `function unaddressableForeignPackages(foreignIndex)` | {Array<{package: string, entries: number, sampleKey: string}>} One finding per drifted package, in the order the index first names each. | Whether a fetched index can still be _addressed_, as distinct from read. |
508
- | `formatUnaddressableFinding` | `function formatUnaddressableFinding(finding, config)` | {string} The formatted diagnostic, path first on the line. | One finding, in the standard `file:line:column: severity: message` form. |
495
+ | Export | Signature | Returns | Use it when |
496
+ | ------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
497
+ | `METADATA_RELATIONSHIP_KINDS` | `const METADATA_RELATIONSHIP_KINDS` | — | The relationship kinds that are dependencies, and therefore citable. |
498
+ | `metadataFileName` | `function metadataFileName(pkg)` | {string} The file name, e.g. | What a package's content index is called, wherever it is written or fetched. |
499
+ | `metadataRelationships` | `function metadataRelationships(config)` | {Array<{id: string, manifest: string, kind: string, verified: string\|undefined}>} The dependencies, in declaration order. | Every dependency whose published index this build resolves addresses through. |
500
+ | `noContentIndexPackages` | `function noContentIndexPackages(config)` | {ReadonlySet<string>} The content package names. | Every package a relationship declares `contentIndex: false` on, keyed by the content package name a link into it would use. |
501
+ | `metadataCacheDir` | `function metadataCacheDir(config, id, version)` | {string} The directory. | The cache directory for one dependency's index at one version. |
502
+ | `isComplete` | `const isComplete` | {boolean} True when it was fetched to completion. | Whether a dependency's cache is present and complete. |
503
+ | `markComplete` | `function markComplete(dir)` | {void} | Mark a dependency's cache complete. |
504
+ | `cachedMetadataFiles` | `function cachedMetadataFiles(config)` | {string[]} One index file per declared dependency. | The fetched index files this build resolves foreign addresses against. |
505
+ | `cachedMetadataIndexes` | `function cachedMetadataIndexes(config)` | {Array<{id: string, file: string}>} One entry per declared dependency. | The same fetched indexes, each paired with the package that published it. |
506
+ | `newestVersionDir` | `function newestVersionDir(dirs)` | {string} The newest one. | The newest cached version among several version-keyed cache directories. |
507
+ | `loadForeignIndexes` | `function loadForeignIndexes(config, localPackages, bases` | {{index: Map<string, object>, packages: Set<string>, stale: Array<{package: string, reason: string}>}} The resolved addresses, which packages contributed, and what could not be read. | Resolve every foreign address this build can cite, from the fetched indexes. |
508
+ | `cachedIndexPath` | `function cachedIndexPath(config, pkg)` | {string} A path to name in a diagnostic. | Where a dependency's fetched index sits, for naming it in a diagnostic. |
509
+ | `unaddressableForeignPackages` | `function unaddressableForeignPackages(foreignIndex)` | {Array<{package: string, entries: number, sampleKey: string}>} One finding per drifted package, in the order the index first names each. | Whether a fetched index can still be _addressed_, as distinct from read. |
510
+ | `formatUnaddressableFinding` | `function formatUnaddressableFinding(finding, config)` | {string} The formatted diagnostic, path first on the line. | One finding, in the standard `file:line:column: severity: message` form. |
509
511
 
510
512
  ### `engine.foundryEntries`
511
513
 
@@ -834,14 +836,14 @@ Shared helpers for the pack compilers in `packages/content-build/`. The HeroicLa
834
836
 
835
837
  Wikilink resolution for the pack compilers. Content notes link to one another with wikilinks rather than file paths:
836
838
 
837
- | Export | Signature | Returns | Use it when |
838
- | -------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
839
- | `resolveItemDocType` | `function resolveItemDocType(qualifier, types)` | {string\|null} The underlying document type, or `null` when the qualifier is not a virtual one. | Reads a qualifier as the **virtual `doc<type>`** form, or reports that it is not one. |
840
- | `readQualifier` | `function readQualifier(target, types, packages)` | {{type: string, shortcode: string, itemDoc: boolean, package?: string, system?: string, reason?: undefined} \| {reason: "unknown-type"} \| null} The resolved qualifier; a `reason` when the target is definitely qualified but names no known type; or `null` when it is not an address at all. | Read a link target as a **qualified** `type-shortcode` reference, or report that it does not parse as one. |
841
- | `anchorPageId` | `function anchorPageId(noteId, anchorSlug)` | {string} A 16-character alphanumeric id. | The deterministic JournalEntryPage id for one anchor: SHA-256 of `"<noteId>-<anchorSlug>"`, base64-encoded, reduced to the 16 alphanumeric characters a Foundry id allows. |
842
- | `buildWikilinkIndex` | `function buildWikilinkIndex(docs, packageId, foreign, contentPackage)` | {{byShortcode: Map<string, object>, types: Set<string>}} `types` is every type the tree actually contains, so a qualifier naming no real type can be told apart from a missing target. | Builds the link-resolution tables for a content tree. |
843
- | `resolveReference` | `function resolveReference(index, ref, hint)` | {{name?: string, uuid?: string, address?: string, subType?: string}\|undefined} The target, or `undefined` where nothing answers. | Resolve one reference — a bare shortcode, a short address or a canonical one — to what a compendium can use. |
844
- | `convertWikilinks` | `function convertWikilinks(markdown,` | {{markdown: string, unresolved: Array<{link: string, target: string, offset: number, reason: string, packages?: string[], anchor?: string}>}} Each `reason` is one of {@link LINK_FINDING_REASONS}, the vocabulary all three resolvers share — `ambiguous` carries the claiming `packages` and `unknown-anchor` the section it named. | Rewrites every wikilink in a markdown body as a Foundry UUID enricher. |
839
+ | Export | Signature | Returns | Use it when |
840
+ | -------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
841
+ | `resolveItemDocType` | `function resolveItemDocType(qualifier, types)` | {string\|null} The underlying document type, or `null` when the qualifier is not a virtual one. | Reads a qualifier as the **virtual `doc<type>`** form, or reports that it is not one. |
842
+ | `readQualifier` | `function readQualifier(target, types, packages, noIndexPackages)` | {{type: string, shortcode: string, itemDoc: boolean, package?: string, system?: string, reason?: undefined} \| {reason: "unknown-type"\|"no-content-index", package?: string} \| null} The resolved qualifier; a `reason` when the target is definitely qualified but names no known type or no fetched index; or `null` when it is not an address at all. | Read a link target as a **qualified** `type-shortcode` reference, or report that it does not parse as one. |
843
+ | `anchorPageId` | `function anchorPageId(noteId, anchorSlug)` | {string} A 16-character alphanumeric id. | The deterministic JournalEntryPage id for one anchor: SHA-256 of `"<noteId>-<anchorSlug>"`, base64-encoded, reduced to the 16 alphanumeric characters a Foundry id allows. |
844
+ | `buildWikilinkIndex` | `function buildWikilinkIndex(docs, packageId, foreign, contentPackage)` | {{byShortcode: Map<string, object>, types: Set<string>}} `types` is every type the tree actually contains, so a qualifier naming no real type can be told apart from a missing target. | Builds the link-resolution tables for a content tree. |
845
+ | `resolveReference` | `function resolveReference(index, ref, hint)` | {{name?: string, uuid?: string, address?: string, subType?: string}\|undefined} The target, or `undefined` where nothing answers. | Resolve one reference — a bare shortcode, a short address or a canonical one — to what a compendium can use. |
846
+ | `convertWikilinks` | `function convertWikilinks(markdown,` | {{markdown: string, unresolved: Array<{link: string, target: string, offset: number, reason: string, packages?: string[], anchor?: string}>}} Each `reason` is one of {@link LINK_FINDING_REASONS}, the vocabulary all three resolvers share — `ambiguous` carries the claiming `packages` and `unknown-anchor` the section it named. | Rewrites every wikilink in a markdown body as a Foundry UUID enricher. |
845
847
 
846
848
  ### `engine.wikilinkSyntax`
847
849
 
@@ -1288,6 +1290,7 @@ console.log(config.stageDir);
1288
1290
  | `resolvePackageBuildConfig` | `resolvePackageBuildConfig(shared)` | `Readonly<PackageBuildConfig>` | validating the `packageBuild:` section from an already-loaded shared configuration — the pure half, usable without touching disk |
1289
1291
  | `loadPackageBuildConfig` | `loadPackageBuildConfig()` | `Readonly<PackageBuildConfig>` | reading and validating the repository's resolved package-build configuration from disk, read fresh on each call rather than cached at import |
1290
1292
  | `checkHomepage` | `checkHomepage(homepage, contentPackage)` | `void` | validating a resolved `homepage` against `contentPackage` — called by `content-build site` before the generated `baseURL` is written, not by `resolvePackageBuildConfig` itself |
1293
+ | `fail` | `fail(where, problem)` | `never` | rejecting a configured value from a sibling module, naming the dotted key path it was written under, so the loader can resolve it to a line and column |
1291
1294
 
1292
1295
  ## `./prettier`
1293
1296
 
package/docs/commands.md CHANGED
@@ -166,7 +166,7 @@ Publish this package's DataModel field sets as `schema.json`.
166
166
  **SYNOPSIS**
167
167
 
168
168
  ```
169
- package-build schema [--check]
169
+ package-build schema
170
170
  ```
171
171
 
172
172
  **DESCRIPTION**
@@ -174,23 +174,20 @@ package-build schema [--check]
174
174
  Publishes the registries named in `packageBuild.schema` as `schema.json`, read
175
175
  by `content-build content-format schema` and, in a consuming package, by
176
176
  `content-build lint`'s emitted-versus-declared check. A repository that
177
- declares no registries has nothing to publish. `--check` compares the
178
- committed file against what the source would produce now instead of
179
- rewriting it, so CI can gate on the file being current. Reads the registries
180
- `packageBuild.schema` names; writes (or checks) `schema.json` at the
181
- repository root.
177
+ declares no registries has nothing to publish. Reads the registries
178
+ `packageBuild.schema` names; writes `build/schema.json`. A release publishes
179
+ it beside the archive and the manifest when `packageBuild.assets` names
180
+ `build/schema.json` (see `package-build release`); a module's
181
+ `content-build deps fetch` keeps the copy from the archive it downloads.
182
182
 
183
183
  **OPTIONS**
184
184
 
185
- | Option | Type | Default | Description |
186
- | --------- | ------- | ------- | ------------------------------------------------------------------------------ |
187
- | `--check` | boolean | `false` | Fail when the committed `schema.json` is out of date rather than rewriting it. |
185
+ None.
188
186
 
189
187
  **EXIT STATUS**
190
188
 
191
- 1 when `--check` finds the committed file does not match what the source
192
- would produce. 1 on any other thrown error. Otherwise 0 — including when
193
- `packageBuild.schema` is empty, which logs and publishes nothing.
189
+ 1 on any thrown error. Otherwise 0 including when `packageBuild.schema` is
190
+ empty, which logs and publishes nothing.
194
191
 
195
192
  **EXAMPLES**
196
193
 
@@ -202,7 +199,7 @@ package-build: no `packageBuild.schema` declared; nothing to publish.
202
199
  **SEE ALSO**
203
200
 
204
201
  `content-build content-format schema`, `content-build lint [root]`,
205
- [Configuration](configuration.md).
202
+ `package-build release`, [Configuration](configuration.md).
206
203
 
207
204
  ### `package-build manifest`
208
205
 
@@ -670,14 +667,17 @@ package-build release [--no-pdf]
670
667
 
671
668
  The artifact name comes from `packageKind` — a system ships as
672
669
  `system.json`'s sibling, a module as `module.json`'s — so no repository
673
- states it a second time. When the package publishes content
674
- (`publish.site: content`), it also builds the content-tree book (see
675
- `content-build pdf`) and reports it alongside the archive; `--no-pdf` skips
676
- that step for a release that has a tree but does not want the book this
677
- time. A book that fails to build is reported, never fatal — the archive
678
- above is the release regardless. Reads the staged package and (for the book)
679
- the content tree; writes `<artifact>.zip` and, unless skipped, the book,
680
- both under `build/dist`.
670
+ states it a second time. It also publishes the content index the manifest
671
+ advertises (`flags.metadataUrl`) and, when the stage carries one,
672
+ `schema.json` a repository that names `build/schema.json` in
673
+ `packageBuild.assets` gets it released beside the archive; one that does not
674
+ gets none. When the package publishes content (`publish.site: content`), it
675
+ also builds the content-tree book (see `content-build pdf`) and reports it
676
+ alongside the archive; `--no-pdf` skips that step for a release that has a
677
+ tree but does not want the book this time. A book that fails to build is
678
+ reported, never fatal — the archive above is the release regardless. Reads
679
+ the staged package and (for the book) the content tree; writes
680
+ `<artifact>.zip` and, unless skipped, the book, both under `build/dist`.
681
681
 
682
682
  **OPTIONS**
683
683
 
@@ -701,7 +701,7 @@ $ package-build release
701
701
 
702
702
  **SEE ALSO**
703
703
 
704
- `content-build pdf`, [Configuration](configuration.md).
704
+ `content-build pdf`, `package-build schema`, [Configuration](configuration.md).
705
705
 
706
706
  ### `package-build deploy <stage>`
707
707