UNPKG

@catbee/utils

Version:

A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.

561 lines 22.2 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; import fs from "fs/promises"; import { createReadStream, createWriteStream } from "fs"; import { tmpdir } from "os"; import path from "path"; import { pipeline } from "stream/promises"; import { randomUUID } from "crypto"; /** * Checks whether a file or directory exists at the given path. * * @param {string} path - The file or directory path to check. * @returns {Promise<boolean>} Resolves to `true` if the path exists, `false` otherwise. */ export function fileExists(path) { return __awaiter(this, void 0, void 0, function () { var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.access(path)]; case 1: _b.sent(); return [2 /*return*/, true]; case 2: _a = _b.sent(); return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); } /** * Reads and parses a JSON file from the specified path. * * @typeParam T - The expected type of the parsed JSON object. * @param {string} path - The path to the JSON file. * @returns {Promise<T | null>} The parsed object, or `null` if reading or parsing fails. */ export function readJsonFile(path) { return __awaiter(this, void 0, void 0, function () { var data, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.readFile(path, "utf-8")]; case 1: data = _b.sent(); return [2 /*return*/, JSON.parse(data)]; case 2: _a = _b.sent(); return [2 /*return*/, null]; case 3: return [2 /*return*/]; } }); }); } /** * Writes a JavaScript object to a file as formatted JSON. * * @param {string} path - The destination file path. * @param {any} data - The object to serialize and write. * @param {number} [space=2] - Number of spaces for JSON formatting. * @returns {Promise<void>} Resolves when writing is complete. */ export function writeJsonFile(path_1, data_1) { return __awaiter(this, arguments, void 0, function (path, data, space) { var json; if (space === void 0) { space = 2; } return __generator(this, function (_a) { switch (_a.label) { case 0: json = JSON.stringify(data, null, space); return [4 /*yield*/, fs.writeFile(path, json, "utf-8")]; case 1: _a.sent(); return [2 /*return*/]; } }); }); } /** * Deletes a file if it exists. * * @param {string} path - The file path to delete. * @returns {Promise<boolean>} Resolves to `true` if the file was deleted or didn't exist, `false` if deletion failed. */ export function deleteFileIfExists(path) { return __awaiter(this, void 0, void 0, function () { var err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.unlink(path)]; case 1: _a.sent(); return [2 /*return*/, true]; case 2: err_1 = _a.sent(); if (err_1.code === "ENOENT") return [2 /*return*/, true]; return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); } /** * Reads a text file from the specified path. * * @param {string} path - The path to the text file. * @param {BufferEncoding} [encoding="utf-8"] - The encoding to use. * @returns {Promise<string | null>} The file contents, or `null` if reading fails. */ export function readTextFile(path_1) { return __awaiter(this, arguments, void 0, function (path, encoding) { var _a; if (encoding === void 0) { encoding = "utf-8"; } return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.readFile(path, encoding)]; case 1: return [2 /*return*/, _b.sent()]; case 2: _a = _b.sent(); return [2 /*return*/, null]; case 3: return [2 /*return*/]; } }); }); } /** * Writes text content to a file. * * @param {string} path - The destination file path. * @param {string} content - The text content to write. * @param {BufferEncoding} [encoding="utf-8"] - The encoding to use. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export function writeTextFile(path_1, content_1) { return __awaiter(this, arguments, void 0, function (path, content, encoding) { var _a; if (encoding === void 0) { encoding = "utf-8"; } return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.writeFile(path, content, encoding)]; case 1: _b.sent(); return [2 /*return*/, true]; case 2: _a = _b.sent(); return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); } /** * Appends text content to a file. * * @param {string} path - The file path to append to. * @param {string} content - The text content to append. * @param {BufferEncoding} [encoding="utf-8"] - The encoding to use. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export function appendTextFile(path_1, content_1) { return __awaiter(this, arguments, void 0, function (path, content, encoding) { var _a; if (encoding === void 0) { encoding = "utf-8"; } return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.appendFile(path, content, encoding)]; case 1: _b.sent(); return [2 /*return*/, true]; case 2: _a = _b.sent(); return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); } /** * Copies a file from source to destination. * * @param {string} source - Source file path. * @param {string} destination - Destination file path. * @param {boolean} [overwrite=false] - Whether to overwrite if destination exists. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export function copyFile(source_1, destination_1) { return __awaiter(this, arguments, void 0, function (source, destination, overwrite) { var flags, _a; if (overwrite === void 0) { overwrite = false; } return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); flags = overwrite ? 0 : fs.constants.COPYFILE_EXCL; return [4 /*yield*/, fs.copyFile(source, destination, flags)]; case 1: _b.sent(); return [2 /*return*/, true]; case 2: _a = _b.sent(); return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); } /** * Renames/moves a file. * * @param {string} oldPath - Current file path. * @param {string} newPath - New file path. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export function moveFile(oldPath, newPath) { return __awaiter(this, void 0, void 0, function () { var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: _c.trys.push([0, 2, , 8]); return [4 /*yield*/, fs.rename(oldPath, newPath)]; case 1: _c.sent(); return [2 /*return*/, true]; case 2: _a = _c.sent(); _c.label = 3; case 3: _c.trys.push([3, 6, , 7]); return [4 /*yield*/, fs.copyFile(oldPath, newPath)]; case 4: _c.sent(); return [4 /*yield*/, fs.unlink(oldPath)]; case 5: _c.sent(); return [2 /*return*/, true]; case 6: _b = _c.sent(); return [2 /*return*/, false]; case 7: return [3 /*break*/, 8]; case 8: return [2 /*return*/]; } }); }); } /** * Gets file stats if the file exists. * * @param {string} path - Path to the file. * @returns {Promise<fs.Stats | null>} File stats object or null if file doesn't exist. */ export function getFileStats(path) { return __awaiter(this, void 0, void 0, function () { var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.stat(path)]; case 1: return [2 /*return*/, _b.sent()]; case 2: _a = _b.sent(); return [2 /*return*/, null]; case 3: return [2 /*return*/]; } }); }); } /** * Creates a temporary file with optional content. * * @param {object} [options] - Options for creating the temp file. * @param {string} [options.prefix="tmp-"] - Filename prefix. * @param {string} [options.extension=""] - File extension. * @param {string} [options.dir] - Directory to create the file in (defaults to OS temp dir). * @param {string | Buffer} [options.content] - Optional content to write to the file. * @returns {Promise<string>} Path to the created temporary file. */ export function createTempFile() { return __awaiter(this, arguments, void 0, function (_a) { var ext, filename, filepath; var _b = _a === void 0 ? {} : _a, _c = _b.prefix, prefix = _c === void 0 ? "tmp-" : _c, _d = _b.extension, extension = _d === void 0 ? "" : _d, _e = _b.dir, dir = _e === void 0 ? tmpdir() : _e, content = _b.content; return __generator(this, function (_f) { switch (_f.label) { case 0: ext = extension ? extension.startsWith(".") ? extension : ".".concat(extension) : ""; filename = "".concat(prefix).concat(randomUUID()).concat(ext); filepath = path.join(dir, filename); if (!content) return [3 /*break*/, 2]; return [4 /*yield*/, fs.writeFile(filepath, content)]; case 1: _f.sent(); return [3 /*break*/, 4]; case 2: return [4 /*yield*/, fs.writeFile(filepath, "")]; case 3: _f.sent(); _f.label = 4; case 4: return [2 /*return*/, filepath]; } }); }); } /** * Streams a file from source to destination. * Useful for large files to avoid loading the entire file into memory. * * @param {string} source - Source file path. * @param {string} destination - Destination file path. * @returns {Promise<void>} Resolves when streaming completes. * @throws {Error} If streaming fails. */ export function streamFile(source, destination) { return __awaiter(this, void 0, void 0, function () { var readStream, writeStream, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: readStream = createReadStream(source); writeStream = createWriteStream(destination); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, pipeline(readStream, writeStream)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_1 = _a.sent(); // Ensure both streams are closed in case of error readStream.destroy(); writeStream.destroy(); throw error_1; case 4: return [2 /*return*/]; } }); }); } /** * Reads a directory and returns file names. * * @param {string} dirPath - Path to the directory. * @param {object} [options] - Options for reading the directory. * @param {boolean} [options.fullPaths=false] - Whether to return full paths. * @param {RegExp} [options.filter] - Optional regex to filter files. * @returns {Promise<string[]>} Array of file names or paths. * @throws {Error} If directory cannot be read. */ export function readDirectory(dirPath_1) { return __awaiter(this, arguments, void 0, function (dirPath, options) { var files, result; if (options === void 0) { options = {}; } return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, fs.readdir(dirPath)]; case 1: files = _a.sent(); result = files; if (options.filter) { result = result.filter(function (file) { return options.filter.test(file); }); } if (options.fullPaths) { result = result.map(function (file) { return path.join(dirPath, file); }); } return [2 /*return*/, result]; } }); }); } /** * Creates a directory if it doesn't exist. * * @param {string} dirPath - Path to the directory. * @param {boolean} [recursive=true] - Whether to create parent directories. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export function createDirectory(dirPath_1) { return __awaiter(this, arguments, void 0, function (dirPath, recursive) { var err_2; if (recursive === void 0) { recursive = true; } return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.mkdir(dirPath, { recursive: recursive })]; case 1: _a.sent(); return [2 /*return*/, true]; case 2: err_2 = _a.sent(); // Consider it success if directory already exists return [2 /*return*/, err_2.code === "EEXIST"]; case 3: return [2 /*return*/]; } }); }); } /** * Safely reads and parses a JSON file with error details. * * @typeParam T - The expected type of the parsed JSON object. * @param {string} path - The path to the JSON file. * @returns {Promise<{ data: T | null; error: Error | null }>} Object with data and error properties. */ export function safeReadJsonFile(path) { return __awaiter(this, void 0, void 0, function () { var content, data, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.readFile(path, "utf-8")]; case 1: content = _a.sent(); try { data = JSON.parse(content); return [2 /*return*/, { data: data, error: null }]; } catch (error) { return [2 /*return*/, { data: null, error: new Error("Invalid JSON in file ".concat(path, ": ").concat(error.message)), }]; } return [3 /*break*/, 3]; case 2: error_2 = _a.sent(); return [2 /*return*/, { data: null, error: new Error("Failed to read file ".concat(path, ": ").concat(error_2.message)), }]; case 3: return [2 /*return*/]; } }); }); } /** * Checks if a path points to a file (not a directory). * * @param {string} path - Path to check. * @returns {Promise<boolean>} True if the path is a file, false otherwise. */ export function isFile(path) { return __awaiter(this, void 0, void 0, function () { var stat, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.stat(path)]; case 1: stat = _b.sent(); return [2 /*return*/, stat.isFile()]; case 2: _a = _b.sent(); return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); } /** * Gets the size of a file in bytes. * * @param {string} path - Path to the file. * @returns {Promise<number>} Size in bytes or -1 if file doesn't exist. */ export function getFileSize(path) { return __awaiter(this, void 0, void 0, function () { var stat, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.stat(path)]; case 1: stat = _b.sent(); return [2 /*return*/, stat.size]; case 2: _a = _b.sent(); return [2 /*return*/, -1]; case 3: return [2 /*return*/]; } }); }); } /** * Reads a file as a Buffer. * * @param {string} path - Path to the file. * @returns {Promise<Buffer | null>} File contents as Buffer or null if reading fails. */ export function readFileBuffer(path) { return __awaiter(this, void 0, void 0, function () { var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.readFile(path)]; case 1: return [2 /*return*/, _b.sent()]; case 2: _a = _b.sent(); return [2 /*return*/, null]; case 3: return [2 /*return*/]; } }); }); } //# sourceMappingURL=fs.utils.js.map