UNPKG

@synet/fs

Version:

Robust, battle-tested filesystem abstraction for Node.js

167 lines (166 loc) 5.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JsonValidationError = exports.JsonStringifyError = exports.JsonParseError = exports.JsonFileSystem = void 0; /** * Type-safe async JSON filesystem that automatically handles JSON parsing/stringification * Wraps any IAsyncFileSystem implementation to provide typed JSON operations * @template T The type of data stored in JSON files */ class JsonFileSystem { constructor(baseFileSystem, options = {}) { this.baseFileSystem = baseFileSystem; this.options = options; } /** * Read and parse JSON file as typed object * @param path File path * @returns Parsed object of type T */ async readJson(path) { const content = await this.baseFileSystem.readFile(path); try { return JSON.parse(content); } catch (error) { throw new JsonParseError(`Failed to parse JSON from ${path}`, error); } } /** * Stringify and write typed object as JSON * @param path File path * @param data Typed data to write */ async writeJson(path, data) { try { const content = JSON.stringify(data, this.options.replacer, this.options.space ?? 2); await this.baseFileSystem.writeFile(path, content); } catch (error) { throw new JsonStringifyError(`Failed to stringify data for ${path}`, error); } } /** * Read and parse JSON file with default fallback * @param path File path * @param defaultValue Default value if file doesn't exist * @returns Parsed object or default value */ async readJsonWithDefault(path, defaultValue) { if (!(await this.baseFileSystem.exists(path))) { return defaultValue; } return this.readJson(path); } /** * Update JSON file with partial data (shallow merge) * @param path File path * @param updates Partial data to merge */ async updateJson(path, updates) { const current = await this.readJson(path); const updated = { ...current, ...updates }; await this.writeJson(path, updated); } /** * Validate JSON structure before writing (if validator provided) * @param path File path * @param data Data to validate and write */ async writeJsonWithValidation(path, data) { if (this.options.validator) { const isValid = this.options.validator(data); if (!isValid) { throw new JsonValidationError(`Data validation failed for ${path}`); } } await this.writeJson(path, data); } /** * Check if JSON file exists and is valid * @param path File path * @returns true if file exists and contains valid JSON */ async isValidJson(path) { if (!(await this.baseFileSystem.exists(path))) { return false; } try { await this.readJson(path); return true; } catch { return false; } } /** * Atomically update JSON file (read-modify-write with custom updater function) * @param path File path * @param updater Function that receives current data and returns updated data */ async atomicUpdate(path, updater) { const current = await this.readJson(path); const updated = updater(current); await this.writeJson(path, updated); } // Expose underlying filesystem operations for convenience get fileSystem() { return this.baseFileSystem; } // Common filesystem operations (delegate to base) async exists(path) { return this.baseFileSystem.exists(path); } async deleteFile(path) { return this.baseFileSystem.deleteFile(path); } async deleteDir(path) { return this.baseFileSystem.deleteDir(path); } async ensureDir(path) { return this.baseFileSystem.ensureDir(path); } async readDir(path) { return this.baseFileSystem.readDir(path); } async chmod(path, mode) { return this.baseFileSystem.chmod(path, mode); } async clear(dirPath) { if (this.baseFileSystem.clear) { return this.baseFileSystem.clear(dirPath); } } } exports.JsonFileSystem = JsonFileSystem; /** * Error thrown when JSON parsing fails */ class JsonParseError extends Error { constructor(message, cause) { super(message); this.cause = cause; this.name = "JsonParseError"; } } exports.JsonParseError = JsonParseError; /** * Error thrown when JSON stringification fails */ class JsonStringifyError extends Error { constructor(message, cause) { super(message); this.cause = cause; this.name = "JsonStringifyError"; } } exports.JsonStringifyError = JsonStringifyError; /** * Error thrown when JSON validation fails */ class JsonValidationError extends Error { constructor(message) { super(message); this.name = "JsonValidationError"; } } exports.JsonValidationError = JsonValidationError;