UNPKG

@synet/fs

Version:

Robust, battle-tested filesystem abstraction for Node.js

157 lines (156 loc) 4.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JsonValidationError = exports.JsonStringifyError = exports.JsonParseError = exports.JsonFileSystem = void 0; /** * Type-safe JSON filesystem that automatically handles JSON parsing/stringification * Wraps any IFileSystem 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 */ readJsonSync(path) { const content = this.baseFileSystem.readFileSync(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 */ writeJsonSync(path, data) { try { const content = JSON.stringify(data, this.options.replacer, this.options.space ?? 2); this.baseFileSystem.writeFileSync(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 */ readJsonSyncWithDefault(path, defaultValue) { if (!this.baseFileSystem.existsSync(path)) { return defaultValue; } return this.readJsonSync(path); } /** * Update JSON file with partial data (shallow merge) * @param path File path * @param updates Partial data to merge */ updateJsonSync(path, updates) { const current = this.readJsonSync(path); const updated = { ...current, ...updates }; this.writeJsonSync(path, updated); } /** * Validate JSON structure before writing (if validator provided) * @param path File path * @param data Data to validate and write */ writeJsonSyncWithValidation(path, data) { if (this.options.validator) { const isValid = this.options.validator(data); if (!isValid) { throw new JsonValidationError(`Data validation failed for ${path}`); } } this.writeJsonSync(path, data); } /** * Check if JSON file exists and is valid * @param path File path * @returns true if file exists and contains valid JSON */ isValidJsonSync(path) { if (!this.baseFileSystem.existsSync(path)) { return false; } try { this.readJsonSync(path); return true; } catch { return false; } } // Expose underlying filesystem operations for convenience get fileSystem() { return this.baseFileSystem; } // Common filesystem operations (delegate to base) existsSync(path) { return this.baseFileSystem.existsSync(path); } deleteFileSync(path) { this.baseFileSystem.deleteFileSync(path); } deleteDirSync(path) { this.baseFileSystem.deleteDirSync(path); } ensureDirSync(path) { this.baseFileSystem.ensureDirSync(path); } readDirSync(path) { return this.baseFileSystem.readDirSync(path); } chmodSync(path, mode) { this.baseFileSystem.chmodSync(path, mode); } clear(dirPath) { if (this.baseFileSystem.clear) { 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;