UNPKG

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
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