UNPKG

@socketsecurity/lib

Version:

Core utilities and infrastructure for Socket.dev security tools

875 lines (874 loc) 29.9 kB
/** * @fileoverview File system utilities with cross-platform path handling. * Provides enhanced fs operations, glob matching, and directory traversal functions. */ import type { Abortable } from 'events'; import type { MakeDirectoryOptions, ObjectEncodingOptions, OpenMode, PathLike } from 'fs'; import type { JsonReviver } from './json'; import { type Remap } from './objects'; /** * Supported text encodings for Node.js Buffers. * Includes ASCII, UTF-8/16, base64, binary, and hexadecimal encodings. */ export type BufferEncoding = 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'base64' | 'base64url' | 'latin1' | 'binary' | 'hex'; /** * Represents any valid JSON content type. */ export type JsonContent = unknown; /** * Options for asynchronous `findUp` operations. */ export interface FindUpOptions { /** * Starting directory for the search. * @default process.cwd() */ cwd?: string | undefined; /** * Only match directories, not files. * @default false */ onlyDirectories?: boolean | undefined; /** * Only match files, not directories. * @default true */ onlyFiles?: boolean | undefined; /** * Abort signal to cancel the search operation. */ signal?: AbortSignal | undefined; } /** * Options for synchronous `findUpSync` operations. */ export interface FindUpSyncOptions { /** * Starting directory for the search. * @default process.cwd() */ cwd?: string | undefined; /** * Directory to stop searching at (inclusive). * When provided, search will stop at this directory even if the root hasn't been reached. */ stopAt?: string | undefined; /** * Only match directories, not files. * @default false */ onlyDirectories?: boolean | undefined; /** * Only match files, not directories. * @default true */ onlyFiles?: boolean | undefined; } /** * Options for checking if a directory is empty. */ export interface IsDirEmptyOptions { /** * Glob patterns for files to ignore when checking emptiness. * Files matching these patterns are not counted. * @default defaultIgnore */ ignore?: string[] | readonly string[] | undefined; } /** * Options for read operations with abort support. */ export interface ReadOptions extends Abortable { /** * Character encoding to use for reading. * @default 'utf8' */ encoding?: BufferEncoding | string | undefined; /** * File system flag for reading behavior. * @default 'r' */ flag?: string | undefined; } /** * Options for reading directories with filtering and sorting. */ export interface ReadDirOptions { /** * Glob patterns for directories to ignore. * @default undefined */ ignore?: string[] | readonly string[] | undefined; /** * Include empty directories in results. * When `false`, empty directories are filtered out. * @default true */ includeEmpty?: boolean | undefined; /** * Sort directory names alphabetically using natural sort order. * @default true */ sort?: boolean | undefined; } /** * Options for reading files with encoding and abort support. * Can be either an options object, an encoding string, or null. */ export type ReadFileOptions = Remap<ObjectEncodingOptions & Abortable & { flag?: OpenMode | undefined; }> | BufferEncoding | null; /** * Options for reading and parsing JSON files. */ export type ReadJsonOptions = Remap<ReadFileOptions & { /** * Whether to throw errors on parse failure. * When `false`, returns `undefined` on error instead of throwing. * @default true */ throws?: boolean | undefined; /** * JSON reviver function to transform parsed values. * Same as the second parameter to `JSON.parse()`. */ reviver?: Parameters<typeof JSON.parse>[1] | undefined; }>; /** * Options for file/directory removal operations. */ export interface RemoveOptions { /** * Force deletion even outside normally safe directories. * When `false`, prevents deletion outside temp, cacache, and ~/.socket. * @default true for safe directories, false otherwise */ force?: boolean | undefined; /** * Maximum number of retry attempts on failure. * @default 3 */ maxRetries?: number | undefined; /** * Recursively delete directories and contents. * @default true */ recursive?: boolean | undefined; /** * Delay in milliseconds between retry attempts. * @default 200 */ retryDelay?: number | undefined; /** * Abort signal to cancel the operation. */ signal?: AbortSignal | undefined; } /** * Options for safe read operations that don't throw on errors. */ export interface SafeReadOptions extends ReadOptions { /** * Default value to return on read failure. * If not provided, `undefined` is returned on error. */ defaultValue?: unknown | undefined; } /** * Options for write operations with encoding and mode control. */ export interface WriteOptions extends Abortable { /** * Character encoding for writing. * @default 'utf8' */ encoding?: BufferEncoding | string | undefined; /** * File mode (permissions) to set. * Uses standard Unix permission bits (e.g., 0o644). * @default 0o666 (read/write for all, respecting umask) */ mode?: number | undefined; /** * File system flag for write behavior. * @default 'w' (create or truncate) */ flag?: string | undefined; } /** * Options for writing JSON files with formatting control. */ export interface WriteJsonOptions extends WriteOptions { /** * End-of-line sequence to use. * @default '\n' * @example * ```ts * // Windows-style line endings * writeJson('data.json', data, { EOL: '\r\n' }) * ``` */ EOL?: string | undefined; /** * Whether to add a final newline at end of file. * @default true */ finalEOL?: boolean | undefined; /** * JSON replacer function to transform values during stringification. * Same as the second parameter to `JSON.stringify()`. */ replacer?: JsonReviver | undefined; /** * Number of spaces for indentation, or string to use for indentation. * @default 2 * @example * ```ts * // Use tabs instead of spaces * writeJson('data.json', data, { spaces: '\t' }) * * // Use 4 spaces for indentation * writeJson('data.json', data, { spaces: 4 }) * ``` */ spaces?: number | string | undefined; } /** * Find a file or directory by traversing up parent directories. * Searches from the starting directory upward to the filesystem root. * Useful for finding configuration files or project roots. * * @param name - Filename(s) to search for * @param options - Search options including cwd and type filters * @returns Normalized absolute path if found, undefined otherwise * * @example * ```ts * // Find package.json starting from current directory * const pkgPath = await findUp('package.json') * * // Find any of multiple config files * const configPath = await findUp(['.config.js', '.config.json']) * * // Find a directory instead of file * const nodeModules = await findUp('node_modules', { onlyDirectories: true }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function findUp(name: string | string[] | readonly string[], options?: FindUpOptions | undefined): Promise<string | undefined>; /** * Synchronously find a file or directory by traversing up parent directories. * Searches from the starting directory upward to the filesystem root or `stopAt` directory. * Useful for finding configuration files or project roots in synchronous contexts. * * @param name - Filename(s) to search for * @param options - Search options including cwd, stopAt, and type filters * @returns Normalized absolute path if found, undefined otherwise * * @example * ```ts * // Find package.json starting from current directory * const pkgPath = findUpSync('package.json') * * // Find .git directory but stop at home directory * const gitPath = findUpSync('.git', { * onlyDirectories: true, * stopAt: process.env.HOME * }) * * // Find any of multiple config files * const configPath = findUpSync(['.eslintrc.js', '.eslintrc.json']) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function findUpSync(name: string | string[] | readonly string[], options?: FindUpSyncOptions | undefined): string; /** * Check if a path is a directory asynchronously. * Returns `true` for directories, `false` for files or non-existent paths. * * @param filepath - Path to check * @returns `true` if path is a directory, `false` otherwise * * @example * ```ts * if (await isDir('./src')) { * console.log('src is a directory') * } * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function isDir(filepath: PathLike): Promise<boolean>; /** * Check if a path is a directory synchronously. * Returns `true` for directories, `false` for files or non-existent paths. * * @param filepath - Path to check * @returns `true` if path is a directory, `false` otherwise * * @example * ```ts * if (isDirSync('./src')) { * console.log('src is a directory') * } * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function isDirSync(filepath: PathLike): boolean; /** * Check if a directory is empty synchronously. * A directory is considered empty if it contains no files after applying ignore patterns. * Uses glob patterns to filter ignored files. * * @param dirname - Directory path to check * @param options - Options including ignore patterns * @returns `true` if directory is empty (or doesn't exist), `false` otherwise * * @example * ```ts * // Check if directory is completely empty * isDirEmptySync('./build') * * // Check if directory is empty, ignoring .DS_Store files * isDirEmptySync('./cache', { ignore: ['.DS_Store'] }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function isDirEmptySync(dirname: PathLike, options?: IsDirEmptyOptions | undefined): boolean; /** * Check if a path is a symbolic link synchronously. * Uses `lstat` to check the link itself, not the target. * * @param filepath - Path to check * @returns `true` if path is a symbolic link, `false` otherwise * * @example * ```ts * if (isSymLinkSync('./my-link')) { * console.log('Path is a symbolic link') * } * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function isSymLinkSync(filepath: PathLike): boolean; /** * Result of file readability validation. * Contains lists of valid and invalid file paths. */ export interface ValidateFilesResult { /** * File paths that passed validation and are readable. */ validPaths: string[]; /** * File paths that failed validation (unreadable, permission denied, or non-existent). * Common with Yarn Berry PnP virtual filesystem, pnpm symlinks, or filesystem race conditions. */ invalidPaths: string[]; } /** * Validate that file paths are readable before processing. * Filters out files from glob results that cannot be accessed (common with * Yarn Berry PnP virtual filesystem, pnpm content-addressable store symlinks, * or filesystem race conditions in CI/CD environments). * * This defensive pattern prevents ENOENT errors when files exist in glob * results but are not accessible via standard filesystem operations. * * @param filepaths - Array of file paths to validate * @returns Object with `validPaths` (readable) and `invalidPaths` (unreadable) * * @example * ```ts * import { validateFiles } from '@socketsecurity/lib/fs' * * const files = ['package.json', '.pnp.cjs/virtual-file.json'] * const { validPaths, invalidPaths } = validateFiles(files) * * console.log(`Valid: ${validPaths.length}`) * console.log(`Invalid: ${invalidPaths.length}`) * ``` * * @example * ```ts * // Typical usage in Socket CLI commands * const packagePaths = await getPackageFilesForScan(targets) * const { validPaths } = validateFiles(packagePaths) * await sdk.uploadManifestFiles(orgSlug, validPaths) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function validateFiles(filepaths: string[] | readonly string[]): ValidateFilesResult; /** * Read directory names asynchronously with filtering and sorting. * Returns only directory names (not files), with optional filtering for empty directories * and glob-based ignore patterns. Results are naturally sorted by default. * * @param dirname - Directory path to read * @param options - Options for filtering and sorting * @returns Array of directory names, empty array on error * * @example * ```ts * // Get all subdirectories, sorted naturally * const dirs = await readDirNames('./packages') * * // Get non-empty directories only * const nonEmpty = await readDirNames('./cache', { includeEmpty: false }) * * // Get directories without sorting * const unsorted = await readDirNames('./src', { sort: false }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function readDirNames(dirname: PathLike, options?: ReadDirOptions | undefined): Promise<string[]>; /** * Read directory names synchronously with filtering and sorting. * Returns only directory names (not files), with optional filtering for empty directories * and glob-based ignore patterns. Results are naturally sorted by default. * * @param dirname - Directory path to read * @param options - Options for filtering and sorting * @returns Array of directory names, empty array on error * * @example * ```ts * // Get all subdirectories, sorted naturally * const dirs = readDirNamesSync('./packages') * * // Get non-empty directories only, ignoring node_modules * const nonEmpty = readDirNamesSync('./src', { * includeEmpty: false, * ignore: ['node_modules'] * }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function readDirNamesSync(dirname: PathLike, options?: ReadDirOptions): string[]; /** * Read a file as binary data asynchronously. * Returns a Buffer without encoding the contents. * Useful for reading images, archives, or other binary formats. * * @param filepath - Path to file * @param options - Read options (encoding is forced to null for binary) * @returns Promise resolving to Buffer containing file contents * * @example * ```ts * // Read an image file * const imageBuffer = await readFileBinary('./image.png') * * // Read with abort signal * const buffer = await readFileBinary('./data.bin', { signal: abortSignal }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function readFileBinary(filepath: PathLike, options?: ReadFileOptions | undefined): Promise<NonSharedBuffer>; /** * Read a file as UTF-8 text asynchronously. * Returns a string with the file contents decoded as UTF-8. * This is the most common way to read text files. * * @param filepath - Path to file * @param options - Read options including encoding and abort signal * @returns Promise resolving to string containing file contents * * @example * ```ts * // Read a text file * const content = await readFileUtf8('./README.md') * * // Read with custom encoding * const content = await readFileUtf8('./data.txt', { encoding: 'utf-8' }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function readFileUtf8(filepath: PathLike, options?: ReadFileOptions | undefined): Promise<string>; /** * Read a file as binary data synchronously. * Returns a Buffer without encoding the contents. * Useful for reading images, archives, or other binary formats. * * @param filepath - Path to file * @param options - Read options (encoding is forced to null for binary) * @returns Buffer containing file contents * * @example * ```ts * // Read an image file * const imageBuffer = readFileBinarySync('./logo.png') * * // Read a compressed file * const gzipData = readFileBinarySync('./archive.gz') * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function readFileBinarySync(filepath: PathLike, options?: ReadFileOptions | undefined): string | NonSharedBuffer; /** * Read a file as UTF-8 text synchronously. * Returns a string with the file contents decoded as UTF-8. * This is the most common way to read text files synchronously. * * @param filepath - Path to file * @param options - Read options including encoding * @returns String containing file contents * * @example * ```ts * // Read a configuration file * const config = readFileUtf8Sync('./config.txt') * * // Read with custom options * const data = readFileUtf8Sync('./data.txt', { encoding: 'utf8' }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function readFileUtf8Sync(filepath: PathLike, options?: ReadFileOptions | undefined): string | NonSharedBuffer; /** * Read and parse a JSON file asynchronously. * Reads the file as UTF-8 text and parses it as JSON. * Optionally accepts a reviver function to transform parsed values. * * @param filepath - Path to JSON file * @param options - Read and parse options * @returns Promise resolving to parsed JSON value, or undefined if throws is false and an error occurs * * @example * ```ts * // Read and parse package.json * const pkg = await readJson('./package.json') * * // Read JSON with custom reviver * const data = await readJson('./data.json', { * reviver: (key, value) => { * if (key === 'date') return new Date(value) * return value * } * }) * * // Don't throw on parse errors * const config = await readJson('./config.json', { throws: false }) * if (config === undefined) { * console.log('Failed to parse config') * } * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function readJson(filepath: PathLike, options?: ReadJsonOptions | string | undefined): Promise<import("./json").JsonValue>; /** * Read and parse a JSON file synchronously. * Reads the file as UTF-8 text and parses it as JSON. * Optionally accepts a reviver function to transform parsed values. * * @param filepath - Path to JSON file * @param options - Read and parse options * @returns Parsed JSON value, or undefined if throws is false and an error occurs * * @example * ```ts * // Read and parse tsconfig.json * const tsconfig = readJsonSync('./tsconfig.json') * * // Read JSON with custom reviver * const data = readJsonSync('./data.json', { * reviver: (key, value) => { * if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}/.test(value)) { * return new Date(value) * } * return value * } * }) * * // Don't throw on parse errors * const config = readJsonSync('./config.json', { throws: false }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function readJsonSync(filepath: PathLike, options?: ReadJsonOptions | string | undefined): import("./json").JsonValue; /** * Invalidate the cached allowed directories. * Called automatically by the paths/rewire module when paths are overridden in tests. * * @internal Used for test rewiring */ export declare function invalidatePathCache(): void; /** * Safely delete a file or directory asynchronously with built-in protections. * Uses `del` for safer deletion that prevents removing cwd and above by default. * Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories. * * @param filepath - Path or array of paths to delete (supports glob patterns) * @param options - Deletion options including force, retries, and recursion * @throws {Error} When attempting to delete protected paths without force option * * @example * ```ts * // Delete a single file * await safeDelete('./temp-file.txt') * * // Delete a directory recursively * await safeDelete('./build', { recursive: true }) * * // Delete multiple paths * await safeDelete(['./dist', './coverage']) * * // Delete with custom retry settings * await safeDelete('./flaky-dir', { maxRetries: 5, retryDelay: 500 }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function safeDelete(filepath: PathLike | PathLike[], options?: RemoveOptions | undefined): Promise<void>; /** * Safely delete a file or directory synchronously with built-in protections. * Uses `del` for safer deletion that prevents removing cwd and above by default. * Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories. * * @param filepath - Path or array of paths to delete (supports glob patterns) * @param options - Deletion options including force, retries, and recursion * @throws {Error} When attempting to delete protected paths without force option * * @example * ```ts * // Delete a single file * safeDeleteSync('./temp-file.txt') * * // Delete a directory recursively * safeDeleteSync('./build', { recursive: true }) * * // Delete multiple paths with globs * safeDeleteSync(['./dist/**', './coverage/**']) * * // Force delete a protected path (use with caution) * safeDeleteSync('./important', { force: true }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function safeDeleteSync(filepath: PathLike | PathLike[], options?: RemoveOptions | undefined): void; /** * Safely create a directory asynchronously, ignoring EEXIST errors. * This function wraps fs.promises.mkdir and handles the race condition where * the directory might already exist, which is common in concurrent code. * * Unlike fs.promises.mkdir with recursive:true, this function: * - Silently ignores EEXIST errors (directory already exists) * - Re-throws all other errors (permissions, invalid path, etc.) * - Works reliably in multi-process/concurrent scenarios * - Defaults to recursive: true for convenient nested directory creation * * @param path - Directory path to create * @param options - Options including recursive (default: true) and mode settings * @returns Promise that resolves when directory is created or already exists * * @example * ```ts * // Create a directory recursively by default, no error if it exists * await safeMkdir('./config') * * // Create nested directories (recursive: true is the default) * await safeMkdir('./data/cache/temp') * * // Create with specific permissions * await safeMkdir('./secure', { mode: 0o700 }) * * // Explicitly disable recursive behavior * await safeMkdir('./single-level', { recursive: false }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function safeMkdir(path: PathLike, options?: MakeDirectoryOptions | undefined): Promise<void>; /** * Safely create a directory synchronously, ignoring EEXIST errors. * This function wraps fs.mkdirSync and handles the race condition where * the directory might already exist, which is common in concurrent code. * * Unlike fs.mkdirSync with recursive:true, this function: * - Silently ignores EEXIST errors (directory already exists) * - Re-throws all other errors (permissions, invalid path, etc.) * - Works reliably in multi-process/concurrent scenarios * - Defaults to recursive: true for convenient nested directory creation * * @param path - Directory path to create * @param options - Options including recursive (default: true) and mode settings * * @example * ```ts * // Create a directory recursively by default, no error if it exists * safeMkdirSync('./config') * * // Create nested directories (recursive: true is the default) * safeMkdirSync('./data/cache/temp') * * // Create with specific permissions * safeMkdirSync('./secure', { mode: 0o700 }) * * // Explicitly disable recursive behavior * safeMkdirSync('./single-level', { recursive: false }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function safeMkdirSync(path: PathLike, options?: MakeDirectoryOptions | undefined): void; /** * Safely read a file asynchronously, returning undefined on error. * Useful when you want to attempt reading a file without handling errors explicitly. * Returns undefined for any error (file not found, permission denied, etc.). * * @param filepath - Path to file * @param options - Read options including encoding and default value * @returns Promise resolving to file contents, or undefined on error * * @example * ```ts * // Try to read a file, get undefined if it doesn't exist * const content = await safeReadFile('./optional-config.txt') * if (content) { * console.log('Config found:', content) * } * * // Read with specific encoding * const data = await safeReadFile('./data.txt', { encoding: 'utf8' }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function safeReadFile(filepath: PathLike, options?: SafeReadOptions | undefined): Promise<NonSharedBuffer>; /** * Safely read a file synchronously, returning undefined on error. * Useful when you want to attempt reading a file without handling errors explicitly. * Returns undefined for any error (file not found, permission denied, etc.). * * @param filepath - Path to file * @param options - Read options including encoding and default value * @returns File contents, or undefined on error * * @example * ```ts * // Try to read a config file * const config = safeReadFileSync('./config.txt') * if (config) { * console.log('Config loaded successfully') * } * * // Read binary file safely * const buffer = safeReadFileSync('./image.png', { encoding: null }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function safeReadFileSync(filepath: PathLike, options?: SafeReadOptions | undefined): string | NonSharedBuffer; /** * Safely get file stats asynchronously, returning undefined on error. * Useful for checking file existence and properties without error handling. * Returns undefined for any error (file not found, permission denied, etc.). * * @param filepath - Path to check * @returns Promise resolving to Stats object, or undefined on error * * @example * ```ts * // Check if file exists and get its stats * const stats = await safeStats('./file.txt') * if (stats) { * console.log('File size:', stats.size) * console.log('Modified:', stats.mtime) * } * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function safeStats(filepath: PathLike): Promise<import("fs").Stats>; /** * Safely get file stats synchronously, returning undefined on error. * Useful for checking file existence and properties without error handling. * Returns undefined for any error (file not found, permission denied, etc.). * * @param filepath - Path to check * @param options - Read options (currently unused but kept for API consistency) * @returns Stats object, or undefined on error * * @example * ```ts * // Check if file exists and get its size * const stats = safeStatsSync('./file.txt') * if (stats) { * console.log('File size:', stats.size) * console.log('Is directory:', stats.isDirectory()) * } * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function safeStatsSync(filepath: PathLike, options?: ReadFileOptions | undefined): import("fs").BigIntStats | import("fs").Stats; /** * Generate a unique filepath by adding number suffix if the path exists. * Appends `-1`, `-2`, etc. before the file extension until a non-existent path is found. * Useful for creating files without overwriting existing ones. * * @param filepath - Desired file path * @returns Normalized unique filepath (original if it doesn't exist, or with number suffix) * * @example * ```ts * // If 'report.pdf' exists, returns 'report-1.pdf' * const uniquePath = uniqueSync('./report.pdf') * * // If 'data.json' and 'data-1.json' exist, returns 'data-2.json' * const path = uniqueSync('./data.json') * * // If 'backup' doesn't exist, returns 'backup' unchanged * const backupPath = uniqueSync('./backup') * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function uniqueSync(filepath: PathLike): string; /** * Write JSON content to a file asynchronously with formatting. * Stringifies the value with configurable indentation and line endings. * Automatically adds a final newline by default for POSIX compliance. * * @param filepath - Path to write to * @param jsonContent - Value to stringify and write * @param options - Write options including formatting and encoding * @returns Promise that resolves when write completes * * @example * ```ts * // Write formatted JSON with default 2-space indentation * await writeJson('./data.json', { name: 'example', version: '1.0.0' }) * * // Write with custom indentation * await writeJson('./config.json', config, { spaces: 4 }) * * // Write with tabs instead of spaces * await writeJson('./data.json', data, { spaces: '\t' }) * * // Write without final newline * await writeJson('./inline.json', obj, { finalEOL: false }) * * // Write with Windows line endings * await writeJson('./win.json', data, { EOL: '\r\n' }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function writeJson(filepath: PathLike, jsonContent: unknown, options?: WriteJsonOptions | string): Promise<void>; /** * Write JSON content to a file synchronously with formatting. * Stringifies the value with configurable indentation and line endings. * Automatically adds a final newline by default for POSIX compliance. * * @param filepath - Path to write to * @param jsonContent - Value to stringify and write * @param options - Write options including formatting and encoding * * @example * ```ts * // Write formatted JSON with default 2-space indentation * writeJsonSync('./package.json', pkg) * * // Write with custom indentation * writeJsonSync('./tsconfig.json', tsconfig, { spaces: 4 }) * * // Write with tabs for indentation * writeJsonSync('./data.json', data, { spaces: '\t' }) * * // Write compacted (no indentation) * writeJsonSync('./compact.json', data, { spaces: 0 }) * ``` */ /*@__NO_SIDE_EFFECTS__*/ export declare function writeJsonSync(filepath: PathLike, jsonContent: unknown, options?: WriteJsonOptions | string | undefined): void;