random-tables-mcp
Version:
An MCP (Model Context Protocol) server for managing and rolling on random-table assets used in tabletop RPGs. Create, update, and roll on random tables with support for nested tables, weighted entries, and range-based results.
160 lines • 5.78 kB
JavaScript
import { RollTemplateEntity, } from '../../../domain/entities/roll-template-entity.js';
import * as fs from 'fs';
import * as path from 'path';
import { promisify } from 'util';
// Promisify fs functions
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const mkdir = promisify(fs.mkdir);
const readdir = promisify(fs.readdir);
const unlink = promisify(fs.unlink);
const access = promisify(fs.access);
/**
* File-based implementation of the RollTemplateRepository interface.
* Stores templates as JSON files in a specified directory.
*/
export class FileRollTemplateRepository {
/**
* Creates a new FileRollTemplateRepository instance.
* @param dataDir The directory where template files will be stored.
*/
constructor(dataDir) {
this.dataDir = dataDir;
}
/**
* Initializes the repository by ensuring the data directory exists.
*/
async initialize() {
try {
await access(this.dataDir);
}
catch {
// Directory doesn't exist, create it
await mkdir(this.dataDir, { recursive: true });
}
}
/**
* Gets the file path for a template ID.
* @param id The template ID.
* @returns The file path.
*/
getFilePath(id) {
return path.join(this.dataDir, `template-${id}.json`);
}
/**
* Saves a template to the repository.
* @param template The template to save.
* @returns The ID of the saved template.
*/
async save(template) {
await this.initialize();
const filePath = this.getFilePath(template.id);
await writeFile(filePath, JSON.stringify(template.toObject(), null, 2), 'utf8');
return template.id;
}
/**
* Gets a template by its ID.
* @param id The ID of the template to get.
* @returns The template, or null if not found.
*/
async getById(id) {
await this.initialize();
const filePath = this.getFilePath(id);
try {
const data = await readFile(filePath, 'utf8');
const templateData = JSON.parse(data);
return RollTemplateEntity.fromObject(templateData);
}
catch {
// File doesn't exist or can't be read
return null;
}
}
/**
* Updates an existing template.
* @param template The updated template.
* @throws Error if the template does not exist.
*/
async update(template) {
await this.initialize();
const filePath = this.getFilePath(template.id);
try {
await access(filePath);
await writeFile(filePath, JSON.stringify(template.toObject(), null, 2), 'utf8');
}
catch {
throw new Error(`Template with ID ${template.id} does not exist`);
}
}
/**
* Lists templates based on optional filter criteria.
* @param filter Optional filter criteria.
* @returns An array of templates matching the filter.
*/
async list(filter) {
await this.initialize();
try {
const files = await readdir(this.dataDir);
const templateFiles = files.filter(file => file.startsWith('template-') && file.endsWith('.json'));
const templates = [];
for (const file of templateFiles) {
try {
const data = await readFile(path.join(this.dataDir, file), 'utf8');
const templateData = JSON.parse(data);
const template = RollTemplateEntity.fromObject(templateData);
templates.push(template);
}
catch (err) {
// Skip files that can't be read or parsed
console.error(`Error reading template file ${file}:`, err);
}
}
if (filter) {
return templates.filter(template => {
// Check if all filter criteria match
return Object.entries(filter).every(([key, value]) => {
// Handle nested properties with dot notation (e.g., "template.template")
const keys = key.split('.');
// Navigate to the nested property
let currentObj = template;
for (let i = 0; i < keys.length - 1; i++) {
currentObj = currentObj[keys[i]];
if (currentObj === undefined)
return false;
}
const prop = keys[keys.length - 1];
// Special case for array length
if (prop === 'length' && Array.isArray(currentObj)) {
return currentObj.length === value;
}
// Regular property comparison
return currentObj[prop] === value;
});
});
}
return templates;
}
catch (err) {
// Error reading directory
console.error('Error listing templates:', err);
return [];
}
}
/**
* Deletes a template by its ID.
* @param id The ID of the template to delete.
* @throws Error if the template does not exist.
*/
async delete(id) {
await this.initialize();
const filePath = this.getFilePath(id);
try {
await access(filePath);
await unlink(filePath);
}
catch {
throw new Error(`Template with ID ${id} does not exist`);
}
}
}
//# sourceMappingURL=file-roll-template-repository.js.map