FileSystem
Defines the portable file system service for Effect programs.
FileSystem is the boundary between Effect code and the host file system.
Platform packages provide concrete layers, while this module defines the
operations for reading, writing, inspecting, streaming, and watching files.
Operations return Effect, Stream, or Sink values and fail with
PlatformError. The module also includes file handles, open flags, watch
events, and the watch backend service.
Constructors
Creates a FileSystem implementation from a partial implementation.
When to use
Use to build a concrete FileSystem service from platform-specific core
operations while deriving the convenience methods that can be implemented
from them.
Details
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.
See
Signature
declare function make(impl: Omit<FileSystem, typeof TypeId | "exists" | "readFileString" | "stream" | "sink" | "writeFileString">): FileSystemCreates a stub FileSystem implementation for tests.
Details
By default, exists returns false, remove succeeds, many file operations
fail with PlatformError NotFound, and temporary-directory/file operations
die as not implemented. Pass method overrides to provide the behavior needed
by a specific test without touching the real file system.
Signature
declare function makeNoop(fileSystem: Partial<FileSystem>): FileSystemExample
(Creating a no-op FileSystem)
import { Effect, FileSystem, PlatformError } from "effect"
// Create a test filesystem that only allows reading specific filesconst 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 testsconst program = Effect.gen(function*() { const content = yield* testFs.readFileString("test-config.json") return content})
// Test with the no-op filesystemconst testProgram = Effect.provideService( program, FileSystem.FileSystem, testFs)Effect.runSync(testProgram) // => "{\"test\": true}"Guards
Returns true if a value is a File handle by checking for the
FileTypeId marker.
When to use
Use when accepting an unknown value and you need to narrow it to a File
before calling file-handle operations.
Details
This is a structural marker check. It does not validate the marker value or the shape of the file handle.
See
- File for the file-handle interface narrowed by this guard
- FileTypeId for the runtime marker checked by this guard
Signature
declare function isFile(u: unknown): u is FileLayers
Creates a Layer that provides a no-op FileSystem implementation for testing.
Details
This is a convenience function that wraps makeNoop in a Layer, making it easy
to provide the test filesystem to your Effect programs.
Signature
declare function layerNoop(fileSystem: Partial<FileSystem>): Layer<FileSystem>Example
(Providing a no-op FileSystem layer)
import { Effect, FileSystem } from "effect"
// Create a test layer with specific behaviorsconst 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 layerconst testProgram = Effect.provide(program, testLayer)Effect.runSync(testProgram) // => "mocked content"Models
Interface representing an open file handle.
Details
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.
Signature
interface File { readonly "~effect/FileSystem/File": "~effect/FileSystem/File"; readonly read: (buffer: Uint8Array) => Effect<number, PlatformError>; readonly readAlloc: (size: number) => Effect<Option<Uint8Array<ArrayBufferLike>>, PlatformError>; readonly seek: (offset: bigint, from: SeekMode) => Effect<bigint, PlatformError>; readonly stat: Effect<Info, PlatformError>; readonly sync: Effect<void, PlatformError>; readonly truncate: (length?: number) => Effect<void, PlatformError>; readonly write: (buffer: Uint8Array) => Effect<number, PlatformError>; readonly writeAll: (buffer: Uint8Array) => Effect<void, PlatformError>;}Example
(Working with file handles)
import { ByteSize, Effect, FileSystem, Option } from "effect"
const file: FileSystem.File = { [FileSystem.FileTypeId]: FileSystem.FileTypeId, stat: Effect.succeed({ size: ByteSize.bytes(5) } as FileSystem.File.Info), seek: () => Effect.succeed(BigInt(0)), sync: Effect.void, read: (buffer) => Effect.sync(() => { buffer.set([1, 2, 3, 4, 5]) return 5 }), readAlloc: () => Effect.succeed(Option.none()), truncate: () => Effect.void, write: (buffer) => Effect.succeed(buffer.length), writeAll: () => Effect.void}
const program = Effect.gen(function*() { const stats = yield* file.stat const buffer = new Uint8Array(5) const bytesRead = yield* file.read(buffer) yield* file.writeAll(new TextEncoder().encode("Hello")) yield* file.sync return { size: stats.size, bytesRead, buffer: Array.from(buffer) }})
const result = Effect.runSync(program)ByteSize.toBigInt(result.size) // => 5nresult.bytesRead // => 5result.buffer // => [1, 2, 3, 4, 5]File open flags that determine how a file is opened and what operations are allowed.
Details
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.
Signature
type OpenFlag = "r" | "r+" | "w" | "wx" | "w+" | "wx+" | "a" | "ax" | "a+" | "ax+"Example
(Opening files with flags)
import type { FileSystem } from "effect"
const flags: ReadonlyArray<FileSystem.OpenFlag> = ["r", "w", "a", "r+"]flags // => ["r", "w", "a", "r+"]Specifies the reference point for seeking within an open file.
When to use
Use with File handles when positioning the cursor before a read or write
and the offset must be interpreted from either the start of the file or the
current cursor.
Details
"start"seeks from the beginning of the file."current"seeks from the current cursor position.
See
- File for the open file handle API whose
seekmethod consumes this mode
Signature
type SeekMode = "start" | "current"WatchEvent type
Represents file system events emitted when watching files or directories.
When to use
Use when consuming file system watch streams and pattern matching on _tag
to handle created, updated, or removed paths.
Details
The union covers create, update, and remove events. Each event carries the
reported path.
See
- FileSystem for the service interface whose
watchoperation emits these events
Signature
type WatchEvent = WatchEvent.Create | WatchEvent.Update | WatchEvent.RemoveWatchOptions interface
Options for watching files or directories.
Signature
interface WatchOptions { readonly recursive?: boolean;}Other
Namespace containing types associated with open file handles, including file descriptors, entry kinds, and stat information.
WatchEvent
Namespace containing the concrete event shapes emitted by FileSystem.watch.
Services
FileSystem
Service tag for platform file-system operations.
When to use
Use to access or provide operations for files, directories, permissions, streams, and sinks through the Effect context.
Details
This key is used to provide and access the FileSystem service in the Effect context.
Signature
declare const FileSystem: Service<FileSystem, FileSystem>Example
(Accessing and providing FileSystem)
import { Effect, FileSystem } from "effect"
const customFs = FileSystem.makeNoop({ exists: () => Effect.succeed(true), readFileString: () => Effect.succeed("contents")})
// Access the FileSystem serviceconst program = Effect.gen(function*() { const fs = yield* FileSystem.FileSystem
const exists = yield* fs.exists("./data.txt") return exists ? yield* fs.readFileString("./data.txt") : undefined})
const withCustomFs = Effect.provideService( program, FileSystem.FileSystem, customFs)Effect.runSync(withCustomFs) // => "contents"FileSystem interface
Core interface for file system operations in Effect.
Details
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.
Signature
interface FileSystem { readonly "~effect/FileSystem": "~effect/FileSystem"; readonly access: (path: string, options?: { readonly ok?: boolean; readonly readable?: boolean; readonly writable?: boolean; }) => Effect<void, PlatformError>; readonly chmod: (path: string, mode: number) => Effect<void, PlatformError>; readonly chown: (path: string, uid: number, gid: number) => Effect<void, PlatformError>; readonly copy: (fromPath: string, toPath: string, options?: { readonly overwrite?: boolean; readonly preserveTimestamps?: boolean; }) => Effect<void, PlatformError>; readonly copyFile: (fromPath: string, toPath: string) => Effect<void, PlatformError>; readonly exists: (path: string) => Effect<boolean, PlatformError>; readonly glob: (pattern: string, options?: { readonly exclude?: readonly Array<string>; readonly root?: string; }) => Effect<Array<string>, PlatformError>; readonly link: (fromPath: string, toPath: string) => Effect<void, PlatformError>; readonly makeDirectory: (path: string, options?: { readonly mode?: number; readonly recursive?: boolean; }) => Effect<void, PlatformError>; readonly makeTempDirectory: (options?: { readonly directory?: string; readonly prefix?: string; }) => Effect<string, PlatformError>; readonly makeTempDirectoryScoped: (options?: { readonly directory?: string; readonly prefix?: string; }) => Effect<string, PlatformError, Scope>; readonly makeTempFile: (options?: { readonly directory?: string; readonly prefix?: string; readonly suffix?: string; }) => Effect<string, PlatformError>; readonly makeTempFileScoped: (options?: { readonly directory?: string; readonly prefix?: string; readonly suffix?: string; }) => Effect<string, PlatformError, Scope>; readonly open: (path: string, options?: { readonly flag?: OpenFlag; readonly mode?: number; }) => Effect<File, PlatformError, Scope>; readonly readDirectory: (path: string, options?: { readonly recursive?: boolean; }) => Effect<Array<string>, PlatformError>; readonly readFile: (path: string) => Effect<Uint8Array<ArrayBufferLike>, PlatformError>; readonly readFileString: (path: string, encoding?: string) => Effect<string, PlatformError>; readonly readLink: (path: string) => Effect<string, PlatformError>; readonly realPath: (path: string) => Effect<string, PlatformError>; readonly remove: (path: string, options?: { readonly force?: boolean; readonly recursive?: boolean; }) => Effect<void, PlatformError>; readonly rename: (oldPath: string, newPath: string) => Effect<void, PlatformError>; readonly sink: (path: string, options?: { readonly flag?: OpenFlag; readonly mode?: number; }) => Sink<void, Uint8Array<ArrayBufferLike>, never, PlatformError>; readonly stat: (path: string) => Effect<Info, PlatformError>; readonly stream: (path: string, options?: { readonly bytesToRead?: Input; readonly chunkSize?: number; readonly offset?: Input; }) => Stream<Uint8Array<ArrayBufferLike>, PlatformError>; readonly symlink: (fromPath: string, toPath: string) => Effect<void, PlatformError>; readonly truncate: (path: string, length?: number) => Effect<void, PlatformError>; readonly utimes: (path: string, atime: number | Date, mtime: number | Date) => Effect<void, PlatformError>; readonly watch: (path: string, options?: WatchOptions) => Stream<WatchEvent, PlatformError>; readonly writeFile: (path: string, data: Uint8Array, options?: { readonly flag?: OpenFlag; readonly mode?: number; }) => Effect<void, PlatformError>; readonly writeFileString: (path: string, data: string, options?: { readonly flag?: OpenFlag; readonly mode?: number; }) => Effect<void, PlatformError>;}Example
(Accessing file system operations)
import { ByteSize, Effect, FileSystem } from "effect"
const fileSystem = FileSystem.makeNoop({ exists: () => Effect.succeed(true), makeDirectory: () => Effect.void, stat: () => Effect.succeed({ size: ByteSize.bytes(22) } as FileSystem.File.Info), readFileString: () => Effect.succeed("{\"env\": \"development\"}")})
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") // Read the file contents const content = yield* fs.readFileString("./config.json") return { size: stats.size, content }})
const result = Effect.runSync(Effect.provideService(program, FileSystem.FileSystem, fileSystem))ByteSize.toBigInt(result.size) // => 22nresult.content // => "{\"env\": \"development\"}"WatchBackend
Service key for file system watch backend implementations.
Details
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.).
Signature
declare class WatchBackend extends Shape<"effect/FileSystem/WatchBackend", { readonly register: (path: string, stat: Info, options?: WatchOptions) => Option<Stream<WatchEvent, PlatformError, never>>;}, this> { constructor(_: never);}Example
(Providing a custom watch backend)
import { Effect, FileSystem, Option, Stream } from "effect"
// Custom watch backend implementationconst customWatchBackend = { register: (path: string, stat: FileSystem.File.Info) => { // Implementation would depend on platform return Option.some(Stream.empty) // Placeholder implementation }}
const program = Effect.gen(function*() { const backend = yield* FileSystem.WatchBackend return Option.isSome( backend.register("./directory", { type: "Directory" } as FileSystem.File.Info) )})
const withCustomBackend = Effect.provideService( program, FileSystem.WatchBackend, customWatchBackend)Effect.runSync(withCustomBackend) // => trueType IDs
FileTypeId
Runtime type identifier attached to FileSystem.File handles and used by
isFile to recognize them.
Details
This marker is part of the runtime representation of file handles. Prefer
isFile when narrowing unknown values.
See
Signature
declare const FileTypeId: "~effect/FileSystem/File"