import * as Brand from "./Brand.ts"; import * as Context from "./Context.ts"; import * as Effect from "./Effect.ts"; import * as Layer from "./Layer.ts"; import * as Option from "./Option.ts"; import { type PlatformError } from "./PlatformError.ts"; import type { Scope } from "./Scope.ts"; import * as Sink from "./Sink.ts"; import * as Stream from "./Stream.ts"; declare const TypeId = "~effect/platform/FileSystem"; /** * Core interface for file system operations in Effect. * * The FileSystem interface provides a comprehensive set of file and directory operations * that work cross-platform. All operations return Effect values that can be composed, * transformed, and executed safely with proper error handling. * * @example * ```ts * import { Console, Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // Basic file operations * const exists = yield* fs.exists("./config.json") * if (!exists) { * yield* fs.writeFileString("./config.json", "{\"env\": \"development\"}") * } * * // Directory operations * yield* fs.makeDirectory("./logs", { recursive: true }) * * // File information * const stats = yield* fs.stat("./config.json") * yield* Console.log(`File size: ${stats.size} bytes`) * * // Streaming operations * const content = yield* fs.readFileString("./config.json") * yield* Console.log("Config:", content) * }) * ``` * * @since 4.0.0 * @category model */ export interface FileSystem { readonly [TypeId]: typeof TypeId; /** * Check if a file can be accessed. * You can optionally specify the level of access to check for. */ readonly access: (path: string, options?: { readonly ok?: boolean | undefined; readonly readable?: boolean | undefined; readonly writable?: boolean | undefined; }) => Effect.Effect; /** * Copy a file or directory from `fromPath` to `toPath`. * * Equivalent to `cp -r`. */ readonly copy: (fromPath: string, toPath: string, options?: { readonly overwrite?: boolean | undefined; readonly preserveTimestamps?: boolean | undefined; }) => Effect.Effect; /** * Copy a file from `fromPath` to `toPath`. */ readonly copyFile: (fromPath: string, toPath: string) => Effect.Effect; /** * Change the permissions of a file. */ readonly chmod: (path: string, mode: number) => Effect.Effect; /** * Change the owner and group of a file. */ readonly chown: (path: string, uid: number, gid: number) => Effect.Effect; /** * Check if a path exists. */ readonly exists: (path: string) => Effect.Effect; /** * Create a hard link from `fromPath` to `toPath`. */ readonly link: (fromPath: string, toPath: string) => Effect.Effect; /** * Create a directory at `path`. You can optionally specify the mode and * whether to recursively create nested directories. */ readonly makeDirectory: (path: string, options?: { readonly recursive?: boolean | undefined; readonly mode?: number | undefined; }) => Effect.Effect; /** * Create a temporary directory. * * By default the directory will be created inside the system's default * temporary directory, but you can specify a different location by setting * the `directory` option. * * You can also specify a prefix for the directory name by setting the * `prefix` option. */ readonly makeTempDirectory: (options?: { readonly directory?: string | undefined; readonly prefix?: string | undefined; }) => Effect.Effect; /** * Create a temporary directory inside a scope. * * Functionally equivalent to `makeTempDirectory`, but the directory will be * automatically deleted when the scope is closed. */ readonly makeTempDirectoryScoped: (options?: { readonly directory?: string | undefined; readonly prefix?: string | undefined; }) => Effect.Effect; /** * Create a temporary file. * The directory creation is functionally equivalent to `makeTempDirectory`. * The file name will be a randomly generated string. */ readonly makeTempFile: (options?: { readonly directory?: string | undefined; readonly prefix?: string | undefined; readonly suffix?: string | undefined; }) => Effect.Effect; /** * Create a temporary file inside a scope. * * Functionally equivalent to `makeTempFile`, but the file will be * automatically deleted when the scope is closed. */ readonly makeTempFileScoped: (options?: { readonly directory?: string | undefined; readonly prefix?: string | undefined; readonly suffix?: string | undefined; }) => Effect.Effect; /** * Open a file at `path` with the specified `options`. * * The file handle will be automatically closed when the scope is closed. */ readonly open: (path: string, options?: { readonly flag?: OpenFlag | undefined; readonly mode?: number | undefined; }) => Effect.Effect; /** * List the contents of a directory. * * You can recursively list the contents of nested directories by setting the * `recursive` option. */ readonly readDirectory: (path: string, options?: { readonly recursive?: boolean | undefined; }) => Effect.Effect, PlatformError>; /** * Read the contents of a file. */ readonly readFile: (path: string) => Effect.Effect; /** * Read the contents of a file. */ readonly readFileString: (path: string, encoding?: string) => Effect.Effect; /** * Read the destination of a symbolic link. */ readonly readLink: (path: string) => Effect.Effect; /** * Resolve a path to its canonicalized absolute pathname. */ readonly realPath: (path: string) => Effect.Effect; /** * Remove a file or directory. */ readonly remove: (path: string, options?: { /** * When `true`, you can recursively remove nested directories. */ readonly recursive?: boolean | undefined; /** * When `true`, exceptions will be ignored if `path` does not exist. */ readonly force?: boolean | undefined; }) => Effect.Effect; /** * Rename a file or directory. */ readonly rename: (oldPath: string, newPath: string) => Effect.Effect; /** * Create a writable `Sink` for the specified `path`. */ readonly sink: (path: string, options?: { readonly flag?: OpenFlag | undefined; readonly mode?: number | undefined; }) => Sink.Sink; /** * Get information about a file at `path`. */ readonly stat: (path: string) => Effect.Effect; /** * Create a readable `Stream` for the specified `path`. * * Changing the `bufferSize` option will change the internal buffer size of * the stream. It defaults to `4`. * * The `chunkSize` option will change the size of the chunks emitted by the * stream. It defaults to 64kb. * * Changing `offset` and `bytesToRead` will change the offset and the number * of bytes to read from the file. */ readonly stream: (path: string, options?: { readonly bytesToRead?: SizeInput | undefined; readonly chunkSize?: SizeInput | undefined; readonly offset?: SizeInput | undefined; }) => Stream.Stream; /** * Create a symbolic link from `fromPath` to `toPath`. */ readonly symlink: (fromPath: string, toPath: string) => Effect.Effect; /** * Truncate a file to a specified length. If the `length` is not specified, * the file will be truncated to length `0`. */ readonly truncate: (path: string, length?: SizeInput) => Effect.Effect; /** * Change the file system timestamps of the file at `path`. */ readonly utimes: (path: string, atime: Date | number, mtime: Date | number) => Effect.Effect; /** * Watch a directory or file for changes */ readonly watch: (path: string) => Stream.Stream; /** * Write data to a file at `path`. */ readonly writeFile: (path: string, data: Uint8Array, options?: { readonly flag?: OpenFlag | undefined; readonly mode?: number | undefined; }) => Effect.Effect; /** * Write a string to a file at `path`. */ readonly writeFileString: (path: string, data: string, options?: { readonly flag?: OpenFlag | undefined; readonly mode?: number | undefined; }) => Effect.Effect; } /** * Represents a file size in bytes using a branded bigint. * * This type ensures type safety when working with file sizes, preventing * accidental mixing of regular numbers with size values. The underlying * bigint allows for handling very large file sizes beyond JavaScript's * number precision limits. * * @example * ```ts * import { Effect, FileSystem } from "effect" * * // Create sizes using the Size constructor * const smallFile = FileSystem.Size(1024) // 1 KB * const largeFile = FileSystem.Size(BigInt("9007199254740992")) // Very large * * // Use with file operations * const truncateToSize = (path: string, size: FileSystem.Size) => * Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * return fs.truncate(path, size) * }) * ``` * * @since 4.0.0 * @category sizes */ export type Size = Brand.Branded; /** * Input type for size parameters that accepts multiple numeric types. * * This union type allows file system operations to accept size values in * different formats for convenience, which are then normalized to the * branded `Size` type internally. * * @example * ```ts * import { Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // All of these are valid SizeInput values * yield* fs.truncate("file1.txt", 1024) // number * yield* fs.truncate("file2.txt", BigInt(2048)) // bigint * yield* fs.truncate("file3.txt", FileSystem.Size(4096)) // Size * }) * ``` * * @since 4.0.0 * @category sizes */ export type SizeInput = bigint | number | Size; /** * Creates a `Size` from various numeric input types. * * Converts numbers, bigints, or existing Size values into a properly * branded Size type. This function handles the conversion and ensures * type safety for file size operations. * * @example * ```ts * import { Effect, FileSystem } from "effect" * * // From number * const size1 = FileSystem.Size(1024) * console.log(typeof size1) // "bigint" * * // From bigint * const size2 = FileSystem.Size(BigInt(2048)) * * // From existing Size (identity) * const size3 = FileSystem.Size(size1) * * // Use in file operations * const readChunk = (path: string, chunkSize: number) => * Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * return fs.stream(path, { * chunkSize: FileSystem.Size(chunkSize) * }) * }) * ``` * * @since 4.0.0 * @category sizes */ export declare const Size: (bytes: SizeInput) => Size; /** * Creates a `Size` representing kilobytes (1024 bytes). * * Converts a number of kilobytes to the equivalent size in bytes. * Uses binary kilobytes (1024 bytes) rather than decimal (1000 bytes). * * @example * ```ts * import { Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // Create a 64 KiB buffer size for streaming * const bufferSize = FileSystem.KiB(64) * * const stream = fs.stream("large-file.txt", { * chunkSize: bufferSize * }) * * // Truncate file to 100 KiB * yield* fs.truncate("data.txt", FileSystem.KiB(100)) * }) * ``` * * @since 4.0.0 * @category sizes */ export declare const KiB: (n: number) => Size; /** * Creates a `Size` representing mebibytes (1024² bytes). * * Converts a number of mebibytes to the equivalent size in bytes. * Uses binary mebibytes (1,048,576 bytes) rather than decimal megabytes. * * @example * ```ts * import { Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // Set a 10 MiB chunk size for large file operations * const largeChunkSize = FileSystem.MiB(10) * * const stream = fs.stream("video.mp4", { * chunkSize: largeChunkSize * }) * * // Check if file is larger than 100 MiB * const stats = yield* fs.stat("archive.zip") * const maxSize = FileSystem.MiB(100) * if (stats.size > maxSize) { * yield* Effect.log("File is very large!") * } * }) * ``` * * @since 4.0.0 * @category sizes */ export declare const MiB: (n: number) => Size; /** * Creates a `Size` representing gibibytes (1024³ bytes). * * Converts a number of gibibytes to the equivalent size in bytes. * Uses binary gibibytes (1,073,741,824 bytes) rather than decimal gigabytes. * * @example * ```ts * import { Console, Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // Check available space before creating large files * const stats = yield* fs.stat(".") * const requiredSpace = FileSystem.GiB(5) * * // Create a large temporary file * const tempFile = yield* fs.makeTempFile({ prefix: "large-" }) * yield* fs.truncate(tempFile, FileSystem.GiB(1)) // 1 GiB file * * yield* Console.log(`Created ${tempFile} with 1 GiB size`) * }) * ``` * * @since 4.0.0 * @category sizes */ export declare const GiB: (n: number) => Size; /** * Creates a `Size` representing tebibytes (1024⁴ bytes). * * Converts a number of tebibytes to the equivalent size in bytes. * Uses binary tebibytes (1,099,511,627,776 bytes) rather than decimal terabytes. * * @example * ```ts * import { Console, Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // Check if we're dealing with very large files * const stats = yield* fs.stat("database-backup.sql") * const oneTiB = FileSystem.TiB(1) * * if (stats.size > oneTiB) { * yield* Console.log("This is a very large database backup!") * * // Use larger chunk sizes for such files * const stream = fs.stream("database-backup.sql", { * chunkSize: FileSystem.MiB(100) // 100 MiB chunks * }) * } * }) * ``` * * @since 4.0.0 * @category sizes */ export declare const TiB: (n: number) => Size; /** * Creates a `Size` representing pebibytes (1024⁵ bytes). * * Converts a number of pebibytes to the equivalent size in bytes. * Uses binary pebibytes (1,125,899,906,842,624 bytes) rather than decimal petabytes. * This function uses BigInt arithmetic to handle the very large numbers involved. * * @example * ```ts * import { Console, Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // For extremely large data processing scenarios * const massiveDataset = FileSystem.PiB(2) // 2 PiB * * // This would typically be used in enterprise/cloud scenarios * yield* Console.log(`Processing ${massiveDataset} bytes of data`) * * // Such large files would require specialized streaming * const stream = fs.stream("massive-dataset.bin", { * chunkSize: FileSystem.GiB(1), // 1 GiB chunks * offset: FileSystem.TiB(100) // Start from 100 TiB offset * }) * }) * ``` * * @since 4.0.0 * @category sizes */ export declare const PiB: (n: number) => Size; /** * File open flags that determine how a file is opened and what operations are allowed. * * These flags correspond to standard POSIX file open modes and control the file access * permissions and behavior when opening files. * * - `"r"` - Read-only. File must exist. * - `"r+"` - Read/write. File must exist. * - `"w"` - Write-only. Truncates file to zero length or creates new file. * - `"wx"` - Like 'w' but fails if file exists. * - `"w+"` - Read/write. Truncates file to zero length or creates new file. * - `"wx+"` - Like 'w+' but fails if file exists. * - `"a"` - Write-only. Appends to file or creates new file. * - `"ax"` - Like 'a' but fails if file exists. * - `"a+"` - Read/write. Appends to file or creates new file. * - `"ax+"` - Like 'a+' but fails if file exists. * * @example * ```ts * import { Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // Open for reading only * const readFile = yield* fs.open("data.txt", { flag: "r" }) * * // Open for writing, truncating existing content * const writeFile = yield* fs.open("output.txt", { flag: "w" }) * * // Open for appending * const appendFile = yield* fs.open("log.txt", { flag: "a" }) * * // Open for read/write, but fail if file doesn't exist * const editFile = yield* fs.open("config.json", { flag: "r+" }) * }) * ``` * * @since 4.0.0 * @category model */ export type OpenFlag = "r" | "r+" | "w" | "wx" | "w+" | "wx+" | "a" | "ax" | "a+" | "ax+"; /** * The service identifier for the FileSystem service. * * This key is used to provide and access the FileSystem service in the Effect context. * Use this to inject file system implementations or access file system operations. * * @example * ```ts * import { Effect, FileSystem } from "effect" * * // Access the FileSystem service * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * const exists = yield* fs.exists("./data.txt") * if (exists) { * const content = yield* fs.readFileString("./data.txt") * yield* Effect.log("File content:", content) * } * }) * * // Provide a custom FileSystem implementation * declare const platformImpl: Omit< * FileSystem.FileSystem, * "exists" | "readFileString" | "stream" | "sink" | "writeFileString" * > * const customFs = FileSystem.make(platformImpl) * * const withCustomFs = Effect.provideService( * program, * FileSystem.FileSystem, * customFs * ) * ``` * * @since 4.0.0 * @category tag */ export declare const FileSystem: Context.Service; /** * Creates a FileSystem implementation from a partial implementation. * * This function takes a partial FileSystem implementation and automatically provides * default implementations for `exists`, `readFileString`, `stream`, `sink`, and * `writeFileString` methods based on the provided core methods. * * @since 4.0.0 * @category constructor */ export declare const make: (impl: Omit) => FileSystem; /** * Creates a no-op FileSystem implementation for testing purposes. * * This function creates a FileSystem where most operations fail with "NotFound" errors, * except for operations that can be safely stubbed. You can override specific methods * by providing them in the `fileSystem` parameter. * * This is useful for testing scenarios where you want to control specific file system * behaviors without affecting the actual file system. * * @example * ```ts * import { Effect, FileSystem, PlatformError } from "effect" * * // Create a test filesystem that only allows reading specific files * const testFs = FileSystem.makeNoop({ * readFileString: (path) => { * if (path === "test-config.json") { * return Effect.succeed("{\"test\": true}") * } * return Effect.fail( * PlatformError.systemError({ * _tag: "NotFound", * module: "FileSystem", * method: "readFileString", * description: "File not found", * pathOrDescriptor: path * }) * ) * }, * exists: (path) => Effect.succeed(path === "test-config.json") * }) * * // Use in tests * const program = Effect.gen(function*() { * const content = yield* testFs.readFileString("test-config.json") * // Will succeed with mocked content * }) * * // Test with the no-op filesystem * const testProgram = Effect.provideService( * program, * FileSystem.FileSystem, * testFs * ) * ``` * * @since 4.0.0 * @category constructor */ export declare const makeNoop: (fileSystem: Partial) => FileSystem; /** * Creates a Layer that provides a no-op FileSystem implementation for testing. * * This is a convenience function that wraps `makeNoop` in a Layer, making it easy * to provide the test filesystem to your Effect programs. * * @example * ```ts * import { Effect, FileSystem } from "effect" * * // Create a test layer with specific behaviors * const testLayer = FileSystem.layerNoop({ * readFileString: (path) => Effect.succeed("mocked content"), * exists: () => Effect.succeed(true) * }) * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * const content = yield* fs.readFileString("any-file.txt") * return content * }) * * // Provide the test layer * const testProgram = Effect.provide(program, testLayer) * ``` * * @since 4.0.0 * @category layers */ export declare const layerNoop: (fileSystem: Partial) => Layer.Layer; /** * @since 4.0.0 * @category File */ export declare const FileTypeId = "~effect/platform/FileSystem/File"; /** * Type guard to check if a value is a File instance. * * This function determines whether the provided value is a valid File * instance by checking for the presence of the File type identifier. * * @since 4.0.0 * @category File */ export declare const isFile: (u: unknown) => u is File; /** * Interface representing an open file handle. * * Provides low-level file operations including reading, writing, seeking, * and retrieving file information. File handles are automatically managed * within scoped operations to ensure proper cleanup. * * @example * ```ts * import { Console, Effect, FileSystem } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // Open a file and work with the handle * yield* Effect.scoped( * Effect.gen(function*() { * const file = yield* fs.open("./data.txt", { flag: "r+" }) * * // Get file information * const stats = yield* file.stat * yield* Console.log(`File size: ${stats.size} bytes`) * * // Read from specific position * yield* file.seek(10, "start") * const buffer = new Uint8Array(5) * const bytesRead = yield* file.read(buffer) * yield* Console.log(`Read ${bytesRead} bytes:`, buffer) * * // Write data * const data = new TextEncoder().encode("Hello") * yield* file.write(data) * yield* file.sync // Flush to disk * }) * ) * }) * ``` * * @since 4.0.0 * @category File */ export interface File { readonly [FileTypeId]: typeof FileTypeId; readonly fd: File.Descriptor; readonly stat: Effect.Effect; readonly seek: (offset: SizeInput, from: SeekMode) => Effect.Effect; readonly sync: Effect.Effect; readonly read: (buffer: Uint8Array) => Effect.Effect; readonly readAlloc: (size: SizeInput) => Effect.Effect, PlatformError>; readonly truncate: (length?: SizeInput) => Effect.Effect; readonly write: (buffer: Uint8Array) => Effect.Effect; readonly writeAll: (buffer: Uint8Array) => Effect.Effect; } /** * @since 4.0.0 * @category File */ export declare namespace File { /** * Branded type for file descriptors. * * File descriptors are numeric handles used by the operating system * to identify open files. The branded type ensures type safety. * * @since 4.0.0 * @category File */ type Descriptor = Brand.Branded; /** * Enumeration of possible file system entry types. * * Represents the different types of entries that can exist in a file system, * from regular files to special device files and symbolic links. * * @since 4.0.0 * @category File */ type Type = "File" | "Directory" | "SymbolicLink" | "BlockDevice" | "CharacterDevice" | "FIFO" | "Socket" | "Unknown"; /** * Comprehensive file information structure. * * Contains metadata about a file or directory including type, timestamps, * permissions, and size information. This structure is returned by file * stat operations. * * @example * ```ts * import { Console, Effect, FileSystem, Option } from "effect" * * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * const info: FileSystem.File.Info = yield* fs.stat("./data.txt") * * yield* Console.log(`File type: ${info.type}`) * yield* Console.log(`File size: ${info.size} bytes`) * yield* Console.log(`Mode: ${info.mode.toString(8)}`) // Octal permissions * * // Handle optional timestamps * const mtime = Option.getOrElse(info.mtime, () => new Date(0)) * yield* Console.log(`Modified: ${mtime.toISOString()}`) * * // Check if it's a regular file * if (info.type === "File") { * yield* Console.log("Processing regular file...") * } * }) * ``` * * @since 4.0.0 * @category File */ interface Info { readonly type: Type; readonly mtime: Option.Option; readonly atime: Option.Option; readonly birthtime: Option.Option; readonly dev: number; readonly ino: Option.Option; readonly mode: number; readonly nlink: Option.Option; readonly uid: Option.Option; readonly gid: Option.Option; readonly rdev: Option.Option; readonly size: Size; readonly blksize: Option.Option; readonly blocks: Option.Option; } } /** * Creates a branded file descriptor. * * File descriptors are integer handles that the operating system uses to identify * open files. This branded type ensures type safety when working with file descriptors. * * @since 4.0.0 * @category constructor */ export declare const FileDescriptor: Brand.Constructor; /** * Specifies the reference point for seeking within a file. * * - `"start"` - Seek from the beginning of the file * - `"current"` - Seek from the current position * * @since 4.0.0 * @category model */ export type SeekMode = "start" | "current"; /** * Represents file system events that can be observed when watching files or directories. * * @since 4.0.0 * @category model */ export type WatchEvent = WatchEvent.Create | WatchEvent.Update | WatchEvent.Remove; /** * @since 4.0.0 * @category model */ export declare namespace WatchEvent { /** * Event representing the creation of a new file or directory. * * This event is triggered when a new file or directory is created * in the watched location. * * @since 4.0.0 * @category model */ interface Create { readonly _tag: "Create"; readonly path: string; } /** * Event representing the modification of an existing file or directory. * * This event is triggered when an existing file or directory is * modified in the watched location. * * @since 4.0.0 * @category model */ interface Update { readonly _tag: "Update"; readonly path: string; } /** * Event representing the deletion of a file or directory. * * This event is triggered when a file or directory is deleted * from the watched location. * * @since 4.0.0 * @category model */ interface Remove { readonly _tag: "Remove"; readonly path: string; } } declare const WatchBackend_base: Context.ServiceClass Option.Option>; }>; /** * Service key for file system watch backend implementations. * * This service provides the low-level file watching capabilities that can be * implemented differently on various platforms (e.g., inotify on Linux, * FSEvents on macOS, etc.). * * @example * ```ts * import { Effect, FileSystem, Option, Stream } from "effect" * * // Custom watch backend implementation * const customWatchBackend = { * register: (path: string, stat: FileSystem.File.Info) => { * // Implementation would depend on platform * return Option.some(Stream.empty) // Placeholder implementation * } * } * * // Provide custom watch backend * const program = Effect.gen(function*() { * const fs = yield* FileSystem.FileSystem * * // File watching will use the custom backend * const watcher = fs.watch("./directory") * }) * * const withCustomBackend = Effect.provideService( * program, * FileSystem.WatchBackend, * customWatchBackend * ) * ``` * * @since 4.0.0 * @category file watcher */ export declare class WatchBackend extends WatchBackend_base { } export {}; //# sourceMappingURL=FileSystem.d.ts.map