Files
blb-throne-of-the-bone-king/socket/contentStore.cjs
T

119 lines
3.1 KiB
JavaScript

/* eslint-env node */
const fs = require('node:fs/promises');
const path = require('node:path');
const defaultResourcePaths = {
credits: path.resolve(__dirname, '..', 'src', 'data', 'credits.json'),
};
function isRecord(value) {
return value != null && typeof value === 'object' && !Array.isArray(value);
}
function isOptionalString(value) {
return value == null || typeof value === 'string';
}
function isCreditsLogo(value) {
return (
isRecord(value) &&
typeof value.title === 'string' &&
typeof value.subtitle === 'string' &&
isOptionalString(value.image)
);
}
function isCreditsContributor(value) {
return (
isRecord(value) &&
typeof value.name === 'string' &&
typeof value.role === 'string' &&
isOptionalString(value.credit) &&
isOptionalString(value.url)
);
}
function isCreditsTechnology(value) {
return (
isRecord(value) &&
typeof value.name === 'string' &&
isOptionalString(value.role) &&
isOptionalString(value.url)
);
}
function isCreditsContent(value) {
return (
isRecord(value) &&
isCreditsLogo(value.mainLogo) &&
Array.isArray(value.contributors) &&
value.contributors.every(isCreditsContributor) &&
Array.isArray(value.technologies) &&
value.technologies.every(isCreditsTechnology) &&
Array.isArray(value.specialThanks) &&
value.specialThanks.every(item => typeof item === 'string')
);
}
function getResourcePath(resourcePaths, resource) {
const resourcePath = resourcePaths[resource];
if (resourcePath == null) {
throw new Error(`Unsupported toolkit content resource: ${String(resource)}`);
}
return resourcePath;
}
function validateResourceContent(resource, content) {
if (resource === 'credits' && isCreditsContent(content)) {
return content;
}
throw new Error(`Invalid toolkit content for resource: ${resource}`);
}
function createJsonContentStore(resourcePaths = defaultResourcePaths) {
async function readJsonResource(resource) {
const resourcePath = getResourcePath(resourcePaths, resource);
const source = await fs.readFile(resourcePath, 'utf8');
const content = validateResourceContent(resource, JSON.parse(source));
const stats = await fs.stat(resourcePath);
return {
content,
resource,
revision: String(Math.round(stats.mtimeMs)),
};
}
async function writeJsonResource(resource, content) {
const resourcePath = getResourcePath(resourcePaths, resource);
const validatedContent = validateResourceContent(resource, content);
const temporaryPath = `${resourcePath}.${process.pid}.${Date.now()}.tmp`;
const formattedContent = `${JSON.stringify(validatedContent, null, 2)}\n`;
await fs.writeFile(temporaryPath, formattedContent, 'utf8');
await fs.rename(temporaryPath, resourcePath);
return readJsonResource(resource);
}
return {
readJsonResource,
resourcePaths,
writeJsonResource,
};
}
const defaultStore = createJsonContentStore();
module.exports = {
createJsonContentStore,
defaultResourcePaths,
isCreditsContent,
readJsonResource: defaultStore.readJsonResource,
writeJsonResource: defaultStore.writeJsonResource,
};