@heroiclands/package-build 23.1.0 → 23.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 +71 -0
- package/bin/content-commands.mjs +68 -149
- package/bin/package-build.mjs +56 -10
- package/ci/check-banners.mjs +42 -6
- package/ci/confirm-npm-version.mjs +135 -0
- package/ci/content-format-reference.mjs +14 -5
- package/config.mjs +1 -0
- package/container.mjs +185 -4
- package/content-config.mjs +333 -210
- package/deploy.mjs +116 -10
- package/docs/api.md +238 -246
- package/docs/authoring/assets.md +11 -5
- package/docs/authoring/frontmatter.md +1 -1
- package/docs/authoring/links-and-markup.md +87 -0
- package/docs/commands.md +138 -99
- package/docs/configuration.md +277 -125
- package/docs/content-format.md +2 -0
- package/docs/diagnostics.md +48 -13
- package/docs/guides/book.md +341 -21
- package/docs/guides/e2e.md +13 -2
- package/docs/guides/icons.md +14 -4
- package/docs/guides/module-project.md +18 -8
- package/docs/guides/release-and-deploy.md +54 -6
- package/docs/guides/site.md +5 -2
- package/docs/project-setup.md +30 -10
- package/docs/reference/format-details.md +373 -170
- package/docs/reference/note-types.md +128 -109
- package/docs/reference/theme-pages.md +31 -6
- package/e2e.mjs +140 -28
- package/engine/actor-compiler.mjs +22 -31
- package/engine/address-charset.mjs +4 -17
- package/engine/address-fields.mjs +1 -1
- package/engine/address-render.mjs +12 -1
- package/engine/address-values.mjs +6 -0
- package/engine/address.mjs +46 -33
- package/engine/art-fields.mjs +30 -5
- package/engine/art-slots.mjs +10 -1
- package/engine/asset-bindings.mjs +12 -1
- package/engine/base-compiler.mjs +34 -65
- package/engine/being-age.mjs +2 -7
- package/engine/being-measurements.mjs +12 -1
- package/engine/calendar-artifacts.mjs +6 -0
- package/engine/calendar-choice.mjs +12 -1
- package/engine/calendar-format.mjs +12 -1
- package/engine/calendar-human.mjs +12 -1
- package/engine/compile-corpus.mjs +3 -1
- package/engine/content-address.mjs +9 -12
- package/engine/content-alerts.mjs +13 -1
- package/engine/content-blocks.mjs +14 -3
- package/engine/content-embeds.mjs +11 -3
- package/engine/content-figures.mjs +9 -0
- package/engine/content-footnotes.mjs +12 -1
- package/engine/content-format-check.mjs +11 -262
- package/engine/content-format.yaml +5 -2
- package/engine/content-images.mjs +3 -0
- package/engine/content-index.mjs +443 -20
- package/engine/content-links.mjs +29 -35
- package/engine/content-lint.mjs +2 -3
- package/engine/content-poetry.mjs +12 -1
- package/engine/content-slug.mjs +2 -14
- package/engine/content-spans.mjs +13 -1
- package/engine/content-tables.mjs +12 -1
- package/engine/culture-choice.mjs +210 -13
- package/engine/data-keys.mjs +19 -16
- package/engine/date-conversion.mjs +12 -1
- package/engine/declaration-check.mjs +9 -0
- package/engine/derived-fields.mjs +6 -7
- package/engine/diagnostics.mjs +34 -4
- package/engine/docs-checks.mjs +9 -0
- package/engine/document-subtypes.mjs +47 -34
- package/engine/event-views.mjs +139 -14
- package/engine/exported-scene.mjs +92 -28
- package/engine/extension-attributes.mjs +12 -1
- package/engine/field-spec.mjs +11 -102
- package/engine/folder-notes.mjs +0 -64
- package/engine/foreign-catalog.mjs +2 -1
- package/engine/format-generated.mjs +9 -0
- package/engine/foundry-entries.mjs +10 -28
- package/engine/frontmatter-lint.mjs +83 -392
- package/engine/frontmatter.mjs +20 -70
- package/engine/generate.mjs +10 -15
- package/engine/heading-attributes.mjs +12 -1
- package/engine/helpers.mjs +41 -102
- package/engine/holdings.mjs +1 -1
- package/engine/homepage.mjs +3 -10
- package/engine/ids.mjs +1 -167
- package/engine/index-records.mjs +6 -4
- package/engine/index.mjs +0 -3
- package/engine/infobox-render.mjs +15 -3
- package/engine/infobox.mjs +13 -14
- package/engine/item-compiler.mjs +4 -10
- package/engine/item-docs.mjs +3 -5
- package/engine/item-registry.mjs +2 -10
- package/engine/journals.mjs +1 -5
- package/engine/literature-notes.mjs +4 -12
- package/engine/macros.mjs +1 -2
- package/engine/map-graphviz.mjs +12 -1
- package/engine/markdown-expressions.mjs +12 -1
- package/engine/markdownlint-run.mjs +567 -0
- package/engine/metadata-index.mjs +42 -12
- package/engine/note-addresses.mjs +143 -35
- package/engine/note-claims.mjs +14 -28
- package/engine/note-dates.mjs +3 -3
- package/engine/note-frontmatter.mjs +6 -0
- package/engine/note-ids.mjs +8 -14
- package/engine/note-package.mjs +8 -25
- package/engine/note-state.mjs +1 -3
- package/engine/note-vocabulary.mjs +148 -84
- package/engine/number-words.mjs +12 -1
- package/engine/pack-config.mjs +15 -25
- package/engine/pack-router.mjs +10 -16
- package/engine/pathnames.mjs +8 -22
- package/engine/pdf-build.mjs +773 -121
- package/engine/pdf-coverage.mjs +188 -0
- package/engine/pdf-index.mjs +54 -7
- package/engine/pdf-pages.mjs +247 -0
- package/engine/pdf-render.mjs +114 -4
- package/engine/pdf-toc.mjs +29 -32
- package/engine/project-init.mjs +46 -6
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +52 -88
- package/engine/readability-lint.mjs +9 -0
- package/engine/reckoning-markers.mjs +12 -1
- package/engine/reference-targets.mjs +7 -0
- package/engine/runtime-only-fields.mjs +5 -8
- package/engine/scene-levels.mjs +2 -2
- package/engine/scenes.mjs +1 -1
- package/engine/shortcode-references.mjs +7 -0
- package/engine/site-build.mjs +30 -16
- package/engine/site-index.mjs +10 -16
- package/engine/site-preview.mjs +6 -0
- package/engine/site-root.mjs +8 -1
- package/engine/social-tie-terms.mjs +12 -1
- package/engine/social-ties.mjs +3 -0
- package/engine/sql-tables.mjs +56 -20
- package/engine/standing-terms.mjs +11 -1
- package/engine/standings.mjs +11 -27
- package/engine/system-block.mjs +39 -141
- package/engine/system-vocabulary.mjs +6 -0
- package/engine/walk-files.mjs +69 -0
- package/engine/web-wikilinks.mjs +23 -3
- package/engine/wikilink-syntax.mjs +32 -21
- package/engine/wikilinks.mjs +27 -16
- package/engine/yaml-lint.mjs +7 -4
- package/githooks/attribution.mjs +8 -0
- package/githooks/commit-message.mjs +9 -0
- package/hm3/actors.mjs +9 -48
- package/hm3/default-item-art.mjs +9 -9
- package/hm3/item-fields.mjs +2 -9
- package/hm3/template-priority.mjs +6 -7
- package/hugo-theme/data/banner-kinds.yaml +78 -0
- package/hugo-theme/data/banners.yaml +2 -3
- package/hugo-theme/layouts/404.html +3 -3
- package/hugo-theme/layouts/partials/banner-url.html +31 -7
- package/hugo-theme/layouts/partials/hero-banner.html +13 -14
- package/hugo-theme/layouts/partials/related.html +1 -1
- package/hugo-theme/layouts/partials/warn-missing-banner.html +1 -1
- package/hugo-theme/static/css/style.css +1 -1
- package/manifest.mjs +11 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +6 -3
- package/release.mjs +83 -18
- package/sohl/actors.mjs +16 -27
- package/sohl/default-item-art.mjs +3 -3
- package/sohl/document-subtypes.mjs +1 -16
- package/sohl/infobox.mjs +2 -3
- package/sohl/item-builders.mjs +5 -8
- package/sohl/item-fields.mjs +5 -35
- package/sohl/items.mjs +0 -4
- package/sohl/kb-passes.mjs +3 -3
- package/types/container.d.mts +116 -5
- package/types/content-config.d.mts +83 -35
- package/types/deploy.d.mts +42 -6
- package/types/e2e.d.mts +35 -3
- package/types/engine/address-charset.d.mts +4 -17
- package/types/engine/address.d.mts +17 -8
- package/types/engine/art-fields.d.mts +7 -4
- package/types/engine/art-slots.d.mts +6 -0
- package/types/engine/base-compiler.d.mts +15 -17
- package/types/engine/content-address.d.mts +9 -12
- package/types/engine/content-embeds.d.mts +4 -2
- package/types/engine/content-format-check.d.mts +0 -64
- package/types/engine/content-index.d.mts +82 -3
- package/types/engine/content-links.d.mts +2 -4
- package/types/engine/culture-choice.d.mts +51 -8
- package/types/engine/data-keys.d.mts +2 -2
- package/types/engine/derived-fields.d.mts +1 -2
- package/types/engine/diagnostics.d.mts +27 -4
- package/types/engine/document-subtypes.d.mts +12 -31
- package/types/engine/event-views.d.mts +14 -2
- package/types/engine/exported-scene.d.mts +3 -2
- package/types/engine/field-spec.d.mts +13 -122
- package/types/engine/folder-notes.d.mts +0 -39
- package/types/engine/foreign-catalog.d.mts +2 -1
- package/types/engine/foundry-entries.d.mts +4 -5
- package/types/engine/frontmatter-lint.d.mts +2 -2
- package/types/engine/frontmatter.d.mts +19 -45
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +25 -42
- package/types/engine/homepage.d.mts +3 -10
- package/types/engine/ids.d.mts +0 -114
- package/types/engine/index-records.d.mts +6 -4
- package/types/engine/index.d.mts +0 -1
- package/types/engine/infobox-render.d.mts +7 -1
- package/types/engine/infobox.d.mts +3 -2
- package/types/engine/item-docs.d.mts +1 -1
- package/types/engine/item-registry.d.mts +2 -4
- package/types/engine/literature-notes.d.mts +1 -1
- package/types/engine/markdownlint-run.d.mts +40 -0
- package/types/engine/metadata-index.d.mts +9 -1
- package/types/engine/note-addresses.d.mts +27 -2
- package/types/engine/note-claims.d.mts +3 -5
- package/types/engine/note-ids.d.mts +2 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-state.d.mts +1 -3
- package/types/engine/note-vocabulary.d.mts +28 -21
- package/types/engine/pack-config.d.mts +2 -6
- package/types/engine/pack-router.d.mts +4 -4
- package/types/engine/pathnames.d.mts +3 -3
- package/types/engine/pdf-build.d.mts +98 -12
- package/types/engine/pdf-coverage.d.mts +60 -0
- package/types/engine/pdf-index.d.mts +34 -5
- package/types/engine/pdf-pages.d.mts +143 -0
- package/types/engine/pdf-render.d.mts +45 -1
- package/types/engine/pdf-toc.d.mts +12 -14
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +11 -10
- package/types/engine/runtime-only-fields.d.mts +2 -2
- package/types/engine/site-build.d.mts +9 -5
- package/types/engine/site-index.d.mts +4 -5
- package/types/engine/site-root.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +31 -2
- package/types/engine/standing-terms.d.mts +2 -1
- package/types/engine/standings.d.mts +7 -9
- package/types/engine/system-block.d.mts +10 -44
- package/types/engine/walk-files.d.mts +15 -0
- package/types/engine/wikilink-syntax.d.mts +18 -19
- package/types/engine/yaml-lint.d.mts +5 -4
- package/types/hm3/actors.d.mts +1 -2
- package/types/hm3/item-fields.d.mts +2 -4
- package/types/hm3/template-priority.d.mts +6 -7
- package/types/manifest.d.mts +3 -2
- package/types/release.d.mts +12 -7
- package/types/sohl/actors.d.mts +2 -1
- package/types/sohl/document-subtypes.d.mts +1 -2
- package/types/sohl/item-fields.d.mts +3 -7
- package/engine/retired-fields.mjs +0 -631
- package/types/engine/retired-fields.d.mts +0 -354
package/deploy.mjs
CHANGED
|
@@ -194,6 +194,65 @@ async function exists(p) {
|
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
/**
|
|
198
|
+
* A previous package tree a deploy could not remove after the swap.
|
|
199
|
+
*
|
|
200
|
+
* @typedef {object} LeftoverTree
|
|
201
|
+
* @property {string} path - The tree, still under its `….old-<pid>` name.
|
|
202
|
+
* @property {string} reason - Why the last attempt to remove it failed.
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Removes a tree the host user cannot, typically by acting as the user that
|
|
207
|
+
* owns its files. Receives the tree and the stage's data root.
|
|
208
|
+
*
|
|
209
|
+
* @callback RemoveTree
|
|
210
|
+
* @param {string} tree - The tree to remove.
|
|
211
|
+
* @param {{dataRoot: string}} where - The data root the tree sits beneath.
|
|
212
|
+
* @returns {void|Promise<void>}
|
|
213
|
+
*/
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @param {unknown} err - What a removal threw.
|
|
217
|
+
* @returns {string} Its message on one line, so it fits in one diagnostic.
|
|
218
|
+
*/
|
|
219
|
+
function reasonOf(err) {
|
|
220
|
+
return (err instanceof Error ? err.message : String(err)).replace(/\s+/g, " ").trim();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Remove the previous tree once the new one is in place.
|
|
225
|
+
*
|
|
226
|
+
* Foundry running in a container writes files owned by the container's user,
|
|
227
|
+
* which under a rootless runtime is a mapped uid the host user cannot unlink.
|
|
228
|
+
* A refusal is handed to `removeTree`, which can act as that user. The install
|
|
229
|
+
* is already complete by then, so a tree neither can remove is reported rather
|
|
230
|
+
* than thrown, and keeps its name so it is never mistaken for anything else.
|
|
231
|
+
*
|
|
232
|
+
* @param {string} old - The previous tree.
|
|
233
|
+
* @param {((tree: string) => void|Promise<void>)|undefined} removeTree - The fallback remover.
|
|
234
|
+
* @returns {Promise<LeftoverTree|null>} The tree left behind, or `null`.
|
|
235
|
+
*/
|
|
236
|
+
async function removePrevious(old, removeTree) {
|
|
237
|
+
try {
|
|
238
|
+
await fs.rm(old, { recursive: true, force: true });
|
|
239
|
+
return null;
|
|
240
|
+
} catch (err) {
|
|
241
|
+
const code = /** @type {NodeJS.ErrnoException} */ (err).code;
|
|
242
|
+
if (!removeTree || (code !== "EACCES" && code !== "EPERM")) {
|
|
243
|
+
return { path: old, reason: reasonOf(err) };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
await removeTree(old);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
return { path: old, reason: reasonOf(err) };
|
|
250
|
+
}
|
|
251
|
+
return (await exists(old)) ?
|
|
252
|
+
{ path: old, reason: "the tree is still present after the fallback removal" }
|
|
253
|
+
: null;
|
|
254
|
+
}
|
|
255
|
+
|
|
197
256
|
/**
|
|
198
257
|
* Mirror the staged build into a local directory via a staged, atomic swap.
|
|
199
258
|
*
|
|
@@ -202,9 +261,13 @@ async function exists(p) {
|
|
|
202
261
|
*
|
|
203
262
|
* @param {string} srcAbs - The staged tree.
|
|
204
263
|
* @param {string} destDir - Where the package installs.
|
|
205
|
-
* @
|
|
264
|
+
* @param {object} [opts]
|
|
265
|
+
* @param {(tree: string) => void|Promise<void>} [opts.removeTree] - Removes a
|
|
266
|
+
* previous tree the host user is refused permission to remove.
|
|
267
|
+
* @returns {Promise<{leftovers: LeftoverTree[]}>} The previous trees that
|
|
268
|
+
* could not be removed; the new one is installed either way.
|
|
206
269
|
*/
|
|
207
|
-
export async function deployLocal(srcAbs, destDir) {
|
|
270
|
+
export async function deployLocal(srcAbs, destDir, { removeTree } = {}) {
|
|
208
271
|
const staging = `${destDir}.staging-${process.pid}`;
|
|
209
272
|
const old = `${destDir}.old-${process.pid}`;
|
|
210
273
|
|
|
@@ -220,7 +283,37 @@ export async function deployLocal(srcAbs, destDir) {
|
|
|
220
283
|
const hadDest = await exists(destDir);
|
|
221
284
|
if (hadDest) await fs.rename(destDir, old);
|
|
222
285
|
await fs.rename(staging, destDir);
|
|
223
|
-
|
|
286
|
+
|
|
287
|
+
// 3. Remove every previous tree beside it: this deployment's own, and any
|
|
288
|
+
// an earlier one could not remove. A leftover still carries a manifest,
|
|
289
|
+
// so Foundry reads it as a second copy of the package.
|
|
290
|
+
const leftovers = [];
|
|
291
|
+
for (const tree of await previousTrees(destDir)) {
|
|
292
|
+
const leftover = await removePrevious(tree, removeTree);
|
|
293
|
+
if (leftover) leftovers.push(leftover);
|
|
294
|
+
}
|
|
295
|
+
return { leftovers };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* The previous trees a local deploy has set aside beside a package:
|
|
300
|
+
* `<id>.old-<pid>`, whichever process's pid it carries.
|
|
301
|
+
*
|
|
302
|
+
* @param {string} destDir - Where the package installs.
|
|
303
|
+
* @returns {Promise<string[]>} Their paths, sorted.
|
|
304
|
+
*/
|
|
305
|
+
async function previousTrees(destDir) {
|
|
306
|
+
const prefix = `${path.basename(destDir)}.old-`;
|
|
307
|
+
const entries = await fs.readdir(path.dirname(destDir), { withFileTypes: true });
|
|
308
|
+
return entries
|
|
309
|
+
.filter(
|
|
310
|
+
(entry) =>
|
|
311
|
+
entry.isDirectory() &&
|
|
312
|
+
entry.name.startsWith(prefix) &&
|
|
313
|
+
/^\d+$/.test(entry.name.slice(prefix.length)),
|
|
314
|
+
)
|
|
315
|
+
.map((entry) => path.join(path.dirname(destDir), entry.name))
|
|
316
|
+
.sort();
|
|
224
317
|
}
|
|
225
318
|
|
|
226
319
|
/**
|
|
@@ -235,7 +328,8 @@ export async function deployLocal(srcAbs, destDir) {
|
|
|
235
328
|
* @param {string} remoteDir - Where the package installs on the host.
|
|
236
329
|
* @param {object} [opts]
|
|
237
330
|
* @param {(uploaded: string) => void} [opts.onUpload] - Per-file progress.
|
|
238
|
-
* @returns {Promise<
|
|
331
|
+
* @returns {Promise<{leftovers: LeftoverTree[]}>} The previous tree when it
|
|
332
|
+
* could not be removed; the new one is installed either way.
|
|
239
333
|
*/
|
|
240
334
|
export async function deployRemote(conn, srcAbs, remoteDir, { onUpload } = {}) {
|
|
241
335
|
const { default: Client } = await import("ssh2-sftp-client");
|
|
@@ -255,7 +349,13 @@ export async function deployRemote(conn, srcAbs, remoteDir, { onUpload } = {}) {
|
|
|
255
349
|
const hadDest = Boolean(await sftp.exists(remoteDir));
|
|
256
350
|
if (hadDest) await sftp.rename(remoteDir, old);
|
|
257
351
|
await sftp.rename(staging, remoteDir);
|
|
258
|
-
if (hadDest)
|
|
352
|
+
if (!hadDest) return { leftovers: [] };
|
|
353
|
+
try {
|
|
354
|
+
await sftp.rmdir(old, true);
|
|
355
|
+
return { leftovers: [] };
|
|
356
|
+
} catch (err) {
|
|
357
|
+
return { leftovers: [{ path: old, reason: reasonOf(err) }] };
|
|
358
|
+
}
|
|
259
359
|
} finally {
|
|
260
360
|
await sftp.end();
|
|
261
361
|
}
|
|
@@ -273,7 +373,10 @@ export async function deployRemote(conn, srcAbs, remoteDir, { onUpload } = {}) {
|
|
|
273
373
|
* @param {NodeJS.ProcessEnv} [opts.env] - The environment to read.
|
|
274
374
|
* @param {string} [opts.prefix] - Fallback variable prefix for SFTP overrides.
|
|
275
375
|
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
276
|
-
* @
|
|
376
|
+
* @param {RemoveTree} [opts.removeTree] - Removes a previous local tree the
|
|
377
|
+
* host user is refused permission to remove.
|
|
378
|
+
* @returns {Promise<{stage: string, destination: string, remote: boolean, leftovers: LeftoverTree[]}>}
|
|
379
|
+
* `leftovers` names each previous tree the deploy installed past but could not remove.
|
|
277
380
|
* @throws {Error} On an unknown stage, or one with no destination configured.
|
|
278
381
|
*/
|
|
279
382
|
export async function deployStage({
|
|
@@ -284,6 +387,7 @@ export async function deployStage({
|
|
|
284
387
|
env = process.env,
|
|
285
388
|
prefix = "SOHL",
|
|
286
389
|
log = () => {},
|
|
390
|
+
removeTree,
|
|
287
391
|
}) {
|
|
288
392
|
const name = resolveStage(stage);
|
|
289
393
|
const envVarName = STAGE_ENV_MAP[name];
|
|
@@ -312,14 +416,16 @@ export async function deployStage({
|
|
|
312
416
|
prefix,
|
|
313
417
|
});
|
|
314
418
|
log(`Deploying ${source} → ${conn.username}@${conn.host}:${remoteDir} (sftp)`);
|
|
315
|
-
await deployRemote(conn, source, remoteDir, {
|
|
419
|
+
const { leftovers } = await deployRemote(conn, source, remoteDir, {
|
|
316
420
|
onUpload: (f) => log(` ${f}`),
|
|
317
421
|
});
|
|
318
|
-
return { stage: name, destination: remoteDir, remote: true };
|
|
422
|
+
return { stage: name, destination: remoteDir, remote: true, leftovers };
|
|
319
423
|
}
|
|
320
424
|
|
|
321
425
|
const destDir = path.join(dataRoot, ...segments);
|
|
322
426
|
log(`Deploying ${source} → ${destDir} (local copy)`);
|
|
323
|
-
await deployLocal(source, destDir
|
|
324
|
-
|
|
427
|
+
const { leftovers } = await deployLocal(source, destDir, {
|
|
428
|
+
removeTree: removeTree && ((tree) => removeTree(tree, { dataRoot })),
|
|
429
|
+
});
|
|
430
|
+
return { stage: name, destination: destDir, remote: false, leftovers };
|
|
325
431
|
}
|