Release Healer Man 0.1.29

This commit is contained in:
phenom committed 2026-09-11 14:26:15 -04:00
1 parent 8c9809d3e0
commit 544c1bee91
120 files changed
+4363 -1036

No files matched your search

+17 -28
View File
@@ -1,7 +1,5 @@
import { createHash } from "node:crypto";
import {
constants,
copyFile,
mkdir,
readFile,
readdir,
@@ -11,6 +9,7 @@ import {
} from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { prepareContentObjects } from "./content-objects.mjs";
const SCRIPT_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
export const PROJECT_ROOT = path.resolve(SCRIPT_DIRECTORY, "../..");
@@ -190,7 +189,7 @@ export async function scanContentSources(sources = DEFAULT_CONTENT_SOURCES) {
function contentVersion(files, packs) {
const digest = createHash("sha256");
for (const file of files) digest.update(`${file.logicalPath}\0${file.sha256}\0${file.bytes}\n`);
for (const file of files) digest.update(`${file.logicalPath}\0${file.sha256}\0${file.bytes}\0${JSON.stringify(file.download ?? null)}\n`);
for (const pack of packs) {
digest.update(`pack\0${pack.id}\0${pack.label}\0${pack.bundledOnAndroid}\0${pack.dependencies.join(",")}\n`);
}
@@ -305,6 +304,7 @@ export function createContentManifest(files, options = {}) {
url: `${baseUrl}/${encodeUrlPath(objectName)}`,
sha256: file.sha256,
bytes: file.bytes,
...(file.download ? { download: file.download } : {}),
};
const pack = packs.get(file.packId) ?? {
id: file.packId,
@@ -351,6 +351,11 @@ export function createContentManifest(files, options = {}) {
}
const sortedPacks = [...packs.values()].sort((left, right) => left.id.localeCompare(right.id));
for (const pack of sortedPacks) {
const objects = new Map(files.filter((file) => file.packId === pack.id)
.map((file) => [file.sha256, file.download?.bytes ?? file.bytes]));
pack.downloadBytes = [...objects.values()].reduce((sum, bytes) => sum + bytes, 0);
}
const version = options.version ?? contentVersion(files, sortedPacks);
return {
@@ -420,24 +425,6 @@ export function assertCampaignDungeonManifest(manifest, definitions) {
}
}
async function copyObjectIfMissing(file, objectDirectory) {
const objectName = `${file.sha256}${file.extension}`;
const destination = path.join(objectDirectory, objectName);
try {
const destinationStats = await stat(destination);
if (destinationStats.size !== file.bytes) throw new Error(`Published object has the wrong size: ${destination}`);
return false;
} catch (error) {
if (error?.code !== "ENOENT") throw error;
}
try {
await copyFile(file.sourcePath, destination, constants.COPYFILE_EXCL);
} catch (error) {
if (error?.code !== "EEXIST") throw error;
}
return true;
}
async function replaceJsonAtomically(destination, value) {
const temporary = `${destination}.${process.pid}.${Date.now()}.tmp`;
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, "utf8");
@@ -456,22 +443,24 @@ export async function publishContent(options = {}) {
...campaignPackDefinitions,
...(options.packDefinitions ?? []),
];
const manifest = createContentManifest(files, { ...options, packDefinitions });
if (campaignDefinitions) assertCampaignDungeonManifest(manifest, campaignDefinitions);
if (campaignDefinitions) assertCampaignDungeonManifest(createContentManifest(files, { ...options, packDefinitions }), campaignDefinitions);
const objectDirectory = path.join(contentDirectory, "objects");
const manifestDirectory = path.join(contentDirectory, "manifests");
await mkdir(objectDirectory, { recursive: true });
await mkdir(manifestDirectory, { recursive: true });
let copiedObjects = 0;
for (const file of files) {
if (await copyObjectIfMissing(file, objectDirectory)) copiedObjects += 1;
}
const prepared = await prepareContentObjects(files, {
directory: objectDirectory,
baseUrl: String(options.baseUrl ?? "/content/objects").replace(/\/+$/, ""),
keepOriginal: true,
});
const manifest = createContentManifest(prepared.files, { ...options, packDefinitions });
await replaceJsonAtomically(path.join(manifestDirectory, `${manifest.version}.json`), manifest);
await replaceJsonAtomically(path.join(contentDirectory, "manifest.json"), manifest);
return {
contentDirectory,
copiedObjects,
copiedObjects: prepared.writtenObjects,
storage: { logicalBytes: prepared.logicalBytes, uniqueBytes: prepared.uniqueBytes, storedBytes: prepared.storedBytes },
manifest,
campaignDungeonPackCount: campaignDefinitions?.length ?? 0,
};