一键导入
effect-filesystem
Use Effect FileSystem for platform-abstract file I/O with Node.js/Bun layers or custom implementations.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use Effect FileSystem for platform-abstract file I/O with Node.js/Bun layers or custom implementations.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Define type-safe RPC contracts with effect/unstable/rpc — Rpc.make payload/success/error/defect schemas, RpcSchema.Stream streaming responses, RpcGroup composition (add/merge/omit/prefix/annotate), and RpcMiddleware.Service definitions shared by client and server. Use when declaring or evolving RPC procedures, building a shared contract package, adding streaming endpoints, or defining auth/observability middleware types.
Consume typed RPC services with Effect's RpcClient — protocol layers (HTTP, WebSocket, TCP, worker, in-memory), RpcSerialization codecs, per-call and ambient headers, streaming calls, interruption, reconnection, and RpcClientError handling. Use when calling an RpcGroup from a client, wiring a client transport + serialization stack, debugging RPC transport failures or reconnects, or testing RPC consumers with RpcTest.
Serve RpcGroup contracts with Effect's RpcServer — handler layers (RpcGroup.toLayer/toLayerHandler), protocol layers (HTTP, WebSocket, TCP, stdio, worker), RpcSerialization, server middleware, streaming results, interruption and shutdown semantics, RpcTest. Use when implementing the server side of an Effect RPC API, mounting RPC on an HttpRouter or existing HTTP app, choosing a wire format, implementing RpcMiddleware, handling client aborts, or testing RPC handlers.
Make outgoing HTTP requests with Effect's HttpClient — HttpClientRequest builders, schema-decoded HttpClientResponse bodies, the HttpClientError taxonomy, retryTransient/rate limiting/cookies/redirects, streaming uploads and downloads, and FetchHttpClient/NodeHttpClient transport layers. Use when calling external REST/JSON APIs, uploading or downloading files and streams, adding retries/auth/tracing to outbound HTTP, or mocking HTTP responses in tests.
Build HTTP servers with effect/unstable/http — HttpRouter routes and middleware, HttpServerRequest schema decoding, HttpServerResponse constructors, multipart uploads, websocket upgrades, static files, NodeHttpServer/BunHttpServer layers, and in-memory web handlers. Use when serving raw HTTP routes, reading request bodies/cookies/uploads, writing server middleware, streaming responses, or testing handlers without a real port.
Build bidirectional socket transports with effect/unstable/socket — the Socket run/writer surface, WebSocket-backed sockets, TCP/Unix-domain clients via NodeSocket, SocketServer accept loops, channel adapters, the SocketError taxonomy, and reconnect patterns. Use when connecting to or serving raw TCP, Unix-domain, or WebSocket endpoints, framing socket bytes with Stream/Channel (NDJSON), building reconnecting socket clients, or providing socket transports to RPC/devtools layers.
| name | effect-filesystem |
| description | Use Effect FileSystem for platform-abstract file I/O with Node.js/Bun layers or custom implementations. |
Use effect FileSystem for platform-abstract file I/O. Stock layers are provided for Node.js and Bun; @effect/platform-browser does not provide a FileSystem layer in beta.74, so browser code needs a custom/injected implementation.
import { FileSystem } from 'effect';
import { Effect } from 'effect';
// Service injection via yield*
const program = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
// Use fs methods here
const content = yield* fs.readFileString('path/to/file.txt');
return content;
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const readBinary = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const bytes = yield* fs.readFile('data.bin');
return bytes; // Uint8Array
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const readText = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const content = yield* fs.readFileString('config.json');
return content; // string
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const streamFile = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
// fs.stream() returns a Stream directly — not an Effect
const stream = fs.stream('large-file.log');
return stream; // Stream<Uint8Array, PlatformError>
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const listFiles = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const entries = yield* fs.readDirectory('src/');
return entries; // ReadonlyArray<string>
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const readLink = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const target = yield* fs.readLink('symlink');
return target; // string
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const writeBinary = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const data = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
yield* fs.writeFile('output.bin', data);
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const writeText = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.writeFileString('output.txt', 'Hello, World!');
});
import { FileSystem } from 'effect';
import { Effect, Stream, pipe } from 'effect';
const writeStream = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
// fs.sink() returns a Sink directly — not an Effect
const sink = fs.sink('output.log');
yield* pipe(
Stream.fromIterable(['line 1\n', 'line 2\n', 'line 3\n']),
Stream.mapEffect((s) => Effect.succeed(new TextEncoder().encode(s))),
Stream.run(sink)
);
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const copyFile = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.copyFile('source.txt', 'dest.txt');
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const copyDir = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.copy('src-dir/', 'dest-dir/');
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const renameFile = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.rename('old-name.txt', 'new-name.txt');
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const removeFile = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.remove('file.txt');
});
// Remove directory recursively
const removeDir = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.remove('directory/', { recursive: true });
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const useFileHandle = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
// File handles are scoped — automatically closed when scope exits
yield* Effect.scoped(
Effect.gen(function* () {
const file = yield* fs.open('data.txt', { flag: 'r' });
// Use file.read(), file.write(), etc.
const buffer = new Uint8Array(1024);
const bytesRead = yield* file.read(buffer);
})
);
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const createDir = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.makeDirectory('new-dir/');
// Recursive directory creation
yield* fs.makeDirectory('path/to/nested/dir/', { recursive: true });
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const useTempDir = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const tempPath = yield* fs.makeTempDirectory();
// Use tempPath
yield* fs.writeFileString(`${tempPath}/temp-file.txt`, 'data');
// Manual cleanup required
yield* fs.remove(tempPath, { recursive: true });
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const useScopedTempDir = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const tempPath = yield* fs.makeTempDirectoryScoped();
// Use tempPath within scope
yield* fs.writeFileString(`${tempPath}/temp-file.txt`, 'data');
// Automatically cleaned up when scope exits
}).pipe(Effect.scoped);
import { FileSystem } from 'effect';
import { Effect, Console } from 'effect';
const getFileInfo = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const info = yield* fs.stat('file.txt');
yield* Console.log(`Type: ${info.type}`);
// "File" | "Directory" | "SymbolicLink" | "BlockDevice" | "CharacterDevice" | "FIFO" | "Socket" | "Unknown"
yield* Console.log(`Size: ${info.size}`); // bigint
yield* Console.log(`Modified: ${info.mtime}`); // Option<Date>
yield* Console.log(`Accessed: ${info.atime}`); // Option<Date>
yield* Console.log(`Created: ${info.birthtime}`); // Option<Date>
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const checkAccess = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
// Check if file exists and is readable
yield* fs.access('file.txt', { readable: true });
// Check writable
yield* fs.access('file.txt', { writable: true });
// Check if file exists (ok)
yield* fs.access('script.sh', { ok: true });
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const fileExists = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const exists = yield* fs.exists('file.txt');
return exists; // boolean
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const resolvePath = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const realPath = yield* fs.realPath('symlink-or-relative-path');
return realPath; // string (absolute path)
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const changeMode = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.chmod('script.sh', 0o755);
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const changeOwner = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.chown('file.txt', 1000, 1000); // uid, gid
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const updateTimes = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const now = new Date();
yield* fs.utimes('file.txt', now, now); // atime, mtime
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const createHardLink = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.link('original.txt', 'hardlink.txt');
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const createSymlink = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.symlink('target.txt', 'symlink.txt');
});
import { FileSystem } from 'effect';
import { Effect, Stream, Console, pipe } from 'effect';
const watchFiles = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
// fs.watch() returns a Stream directly — not an Effect
const events = fs.watch('src/');
return events; // Stream<WatchEvent, PlatformError>
});
// Consume watch events
const consumeWatchEvents = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const events = fs.watch('config/');
yield* pipe(
events,
Stream.runForEach((event) =>
Console.log(`Event: ${event._tag}, Path: ${event.path}`)
)
);
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const { Size, KiB, MiB, GiB, TiB, PiB } = FileSystem;
// Create size values
const oneKb = Size(1024);
const tenKb = KiB(10);
const oneMb = MiB(1);
const fiveGb = GiB(5);
const oneTb = TiB(1);
const onePb = PiB(1);
// Use with file operations
const checkFileSize = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const info = yield* fs.stat('large-file.bin');
const maxSize = MiB(100);
if (info.size > BigInt(maxSize)) {
yield* Effect.fail(new Error('File too large'));
}
});
FileSystem operations fail with PlatformError containing a SystemErrorTag:
AlreadyExists - File/directory already existsBadResource - Invalid file descriptor or handleBusy - Resource is busyInvalidData - Invalid data formatNotFound - File/directory not foundPermissionDenied - Insufficient permissionsTimedOut - Operation timed outUnexpectedEof - Unexpected end of fileUnknown - Unknown errorWouldBlock - Operation would blockWriteZero - Write operation wrote zero bytesimport { FileSystem } from 'effect';
import { Effect, pipe } from 'effect';
const readConfigWithFallback = pipe(
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
return yield* fs.readFileString('config.json');
}),
Effect.catchTag('PlatformError', (error) => {
if (error.reason._tag === 'NotFound') {
return Effect.succeed('{}');
}
if (error.reason._tag === 'PermissionDenied') {
return Effect.fail(
new Error('Cannot read config: permission denied')
);
}
return Effect.fail(error);
})
);
import { FileSystem } from 'effect';
import { Effect, Schema, pipe } from 'effect';
class ConfigNotFound extends Schema.TaggedErrorClass<ConfigNotFound>()(
'ConfigNotFound',
{
path: Schema.String
}
) {}
class ConfigInvalid extends Schema.TaggedErrorClass<ConfigInvalid>()(
'ConfigInvalid',
{
path: Schema.String,
reason: Schema.String
}
) {}
const readConfig = (path: string) =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const content = yield* pipe(
fs.readFileString(path),
Effect.mapError((error) =>
error.reason._tag === 'NotFound'
? new ConfigNotFound({ path })
: new ConfigInvalid({ path, reason: error.message })
)
);
return content;
});
import { FileSystem } from 'effect';
import { Effect } from 'effect';
const processInTempDir = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
// Create temp directory with automatic cleanup
const tempDir = yield* fs.makeTempDirectoryScoped();
// Do work in temp directory
const inputPath = `${tempDir}/input.txt`;
const outputPath = `${tempDir}/output.txt`;
yield* fs.writeFileString(inputPath, 'data');
const content = yield* fs.readFileString(inputPath);
yield* fs.writeFileString(outputPath, content.toUpperCase());
const result = yield* fs.readFileString(outputPath);
// Temp directory is automatically removed when scope exits
return result;
}).pipe(Effect.scoped);
import { FileSystem } from 'effect';
import { NodeFileSystem } from '@effect/platform-node';
import { Effect } from 'effect';
const program = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
return yield* fs.readFileString('data.txt');
});
// Provide Node.js implementation
const runnable = program.pipe(Effect.provide(NodeFileSystem.layer));
Effect.runPromise(runnable);
import { FileSystem } from 'effect';
import { BunFileSystem } from '@effect/platform-bun';
import { Effect } from 'effect';
declare const program: Effect.Effect<string, never, FileSystem.FileSystem>;
const runnable = program.pipe(Effect.provide(BunFileSystem.layer));
Effect.runPromise(runnable);
effectyield* FileSystem.FileSystem for service injectionmakeTempDirectoryScopedPlatformError with catchTag("PlatformError", ...)Size(), KiB(), MiB(), GiB(), TiB(), PiB()stream() and sink()node:fs, fs/promises, or platform-specific modules in business logicDate.now() - use Clock service instead (see testability requirements)Path service for path operations