Skip to content
Effect Days 2026 Get your ticket

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, size helpers, open flags, watch events, and the watch backend service.

23 exports Added in v4.0.0 Source

Constructors

make

Added in v4.0.0 Source

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

  • makeNoop for a testing stub that accepts method overrides without requiring a complete implementation
  • layerNoop for providing a no-op FileSystem as a Layer in tests

Signature

declare function make(impl: Omit<FileSystem, typeof TypeId | "exists" | "readFileString" | "stream" | "sink" | "writeFileString">): FileSystem

makeNoop

Added in v4.0.0 Source

Creates 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>): FileSystem

Example

(Creating a no-op FileSystem)

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")
return content
})
// Test with the no-op filesystem
const testProgram = Effect.provideService(
program,
FileSystem.FileSystem,
testFs
)
Effect.runSync(testProgram) // => "{\"test\": true}"

Guards

isFile

Added in v4.0.0 Source

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 File

Layers

layerNoop

Added in v4.0.0 Source

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 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)
Effect.runSync(testProgram) // => "mocked content"

Models

File interface

Added in v4.0.0 Source

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/platform/FileSystem/File": "~effect/platform/FileSystem/File";
readonly read: (buffer: Uint8Array) => Effect<Size, PlatformError>;
readonly readAlloc: (size: SizeInput) => Effect<Option<Uint8Array<ArrayBufferLike>>, PlatformError>;
readonly seek: (offset: SizeInput, from: SeekMode) => Effect<Size>;
readonly stat: Effect<Info, PlatformError>;
readonly sync: Effect<void, PlatformError>;
readonly truncate: (length?: SizeInput) => Effect<void, PlatformError>;
readonly write: (buffer: Uint8Array) => Effect<Size, PlatformError>;
readonly writeAll: (buffer: Uint8Array) => Effect<void, PlatformError>;
}

Example

(Working with file handles)

import { Effect, FileSystem, Option } from "effect"
const file: FileSystem.File = {
[FileSystem.FileTypeId]: FileSystem.FileTypeId,
stat: Effect.succeed({ size: FileSystem.Size(5) } as FileSystem.File.Info),
seek: () => Effect.succeed(FileSystem.Size(0)),
sync: Effect.void,
read: (buffer) => Effect.sync(() => {
buffer.set([1, 2, 3, 4, 5])
return FileSystem.Size(5)
}),
readAlloc: () => Effect.succeed(Option.none()),
truncate: () => Effect.void,
write: (buffer) => Effect.succeed(FileSystem.Size(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) }
})
Effect.runSync(program) // => { size: 5n, bytesRead: 5n, buffer: [1, 2, 3, 4, 5] }

OpenFlag type

Added in v4.0.0 Source

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+"]

SeekMode type

Added in v4.0.0 Source

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 seek method consumes this mode

Signature

type SeekMode = "start" | "current"

WatchEvent type

Added in v4.0.0 Source

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 watch operation emits these events

Signature

type WatchEvent = WatchEvent.Create | WatchEvent.Update | WatchEvent.Remove

WatchOptions interface

Added in v4.0.0 Source

Options for watching files or directories.

Signature

interface WatchOptions {
readonly recursive?: boolean;
}

Other

File

Added in v4.0.0 Source

Namespace containing types associated with open file handles, including file descriptors, entry kinds, and stat information.

WatchEvent

Added in v4.0.0 Source

Namespace containing the concrete event shapes emitted by FileSystem.watch.

Services

FileSystem

Added in v4.0.0 Source

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 service
const 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

Added in v4.0.0 Source

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/platform/FileSystem": "~effect/platform/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?: SizeInput;
readonly chunkSize?: SizeInput;
readonly offset?: SizeInput;
}) => Stream<Uint8Array<ArrayBufferLike>, PlatformError>;
readonly symlink: (fromPath: string, toPath: string) => Effect<void, PlatformError>;
readonly truncate: (path: string, length?: SizeInput) => 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 { Effect, FileSystem } from "effect"
const fileSystem = FileSystem.makeNoop({
exists: () => Effect.succeed(true),
makeDirectory: () => Effect.void,
stat: () => Effect.succeed({ size: FileSystem.Size(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))
result.size // => 22n
result.content // => "{\"env\": \"development\"}"

WatchBackend

Added in v4.0.0 Source

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/platform/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 implementation
const 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) // => true

Sizes

GiB

Added in v4.0.0 Source

Creates a Size representing gibibytes (1024³ bytes).

Details

Converts a number of gibibytes to the equivalent size in bytes. Uses binary gibibytes (1,073,741,824 bytes) rather than decimal gigabytes.

Signature

declare function GiB(n: number): Size

Example

(Creating gibibyte sizes)

import { FileSystem } from "effect"
FileSystem.GiB(1) // => 1073741824n

KiB

Added in v4.0.0 Source

Creates a Size representing kilobytes (1024 bytes).

Details

Converts a number of kilobytes to the equivalent size in bytes. Uses binary kilobytes (1024 bytes) rather than decimal (1000 bytes).

Signature

declare function KiB(n: number): Size

Example

(Creating kibibyte sizes)

import { FileSystem } from "effect"
FileSystem.KiB(64) // => 65536n
FileSystem.KiB(100) // => 102400n

MiB

Added in v4.0.0 Source

Creates a Size representing mebibytes (1024² bytes).

Details

Converts a number of mebibytes to the equivalent size in bytes. Uses binary mebibytes (1,048,576 bytes) rather than decimal megabytes.

Signature

declare function MiB(n: number): Size

Example

(Creating mebibyte sizes)

import { FileSystem } from "effect"
FileSystem.MiB(10) // => 10485760n
FileSystem.MiB(100) // => 104857600n

PiB

Added in v4.0.0 Source

Creates a Size representing pebibytes (1024⁵ bytes).

Details

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.

Signature

declare function PiB(n: number): Size

Example

(Creating pebibyte sizes)

import { FileSystem } from "effect"
FileSystem.PiB(2) // => 2251799813685248n

Size

Added in v4.0.0 Source

Creates a Size from various numeric input types.

Details

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.

Signature

declare const Size: (bytes: SizeInput) => Size

Example

(Converting size inputs)

import { FileSystem } from "effect"
// From number
const size1 = FileSystem.Size(1024)
typeof size1 // => "bigint"
// From bigint
const size2 = FileSystem.Size(BigInt(2048))
// From existing Size (identity)
const size3 = FileSystem.Size(size1)
const sizes = [size2, size3] // => [2048n, 1024n]

Size type

Added in v4.0.0 Source

Represents a file size in bytes using a branded bigint.

Details

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.

Signature

type Size = Brand.Branded<bigint, "Size">

Example

(Creating branded file sizes)

import { FileSystem } from "effect"
FileSystem.Size(1024) // => 1024n
FileSystem.Size(BigInt("9007199254740992")) // => 9007199254740992n

SizeInput type

Added in v4.0.0 Source

Input type for size parameters that accepts multiple numeric types.

Details

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.

Signature

type SizeInput = bigint | number | Size

Example

(Using size inputs)

import { FileSystem } from "effect"
const inputs: ReadonlyArray<FileSystem.SizeInput> = [
1024,
2048n,
FileSystem.Size(4096)
]
inputs.map(FileSystem.Size) // => [1024n, 2048n, 4096n]

TiB

Added in v4.0.0 Source

Creates a Size representing tebibytes (1024⁴ bytes).

Details

Converts a number of tebibytes to the equivalent size in bytes. Uses binary tebibytes (1,099,511,627,776 bytes) rather than decimal terabytes.

Signature

declare function TiB(n: number): Size

Example

(Creating tebibyte sizes)

import { FileSystem } from "effect"
FileSystem.TiB(1) // => 1099511627776n

Type IDs

FileTypeId

Added in v4.0.0 Source

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

  • File for the open file handle shape that carries this marker
  • isFile for the public guard that checks this marker

Signature

declare const FileTypeId: "~effect/platform/FileSystem/File"