Merge commit '36c66dd290d3ce6eb1ccd310d0c658d4a32bb8eb' as 'repos/effect'
This commit is contained in:
@@ -0,0 +1,668 @@
|
||||
/**
|
||||
* Shared Node-compatible implementation of Effect's `FileSystem` service.
|
||||
*
|
||||
* This module adapts Node's `node:fs`, `node:os`, and `node:path` APIs into a
|
||||
* `FileSystem` layer for Effect programs running on Node-compatible runtimes.
|
||||
* Platform packages use it to provide file and directory I/O, permissions,
|
||||
* links, metadata, temporary files and directories, and file watching through
|
||||
* the shared `FileSystem` service.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
import * as Cause from "effect/Cause"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { effectify } from "effect/Effect"
|
||||
import * as FileSystem from "effect/FileSystem"
|
||||
import { pipe } from "effect/Function"
|
||||
import * as Layer from "effect/Layer"
|
||||
import * as Option from "effect/Option"
|
||||
import * as Error from "effect/PlatformError"
|
||||
import * as Queue from "effect/Queue"
|
||||
import * as Stream from "effect/Stream"
|
||||
import * as Crypto from "node:crypto"
|
||||
import * as NFS from "node:fs"
|
||||
import * as OS from "node:os"
|
||||
import * as Path from "node:path"
|
||||
import { handleErrnoException } from "./internal/utils.ts"
|
||||
|
||||
const handleBadArgument = (method: string) => (err: unknown) =>
|
||||
Error.badArgument({
|
||||
module: "FileSystem",
|
||||
method,
|
||||
description: (err as Error).message ?? String(err)
|
||||
})
|
||||
|
||||
// == access
|
||||
|
||||
const access = ((): FileSystem.FileSystem["access"] => {
|
||||
const nodeAccess = effectify(
|
||||
NFS.access,
|
||||
handleErrnoException("FileSystem", "access"),
|
||||
handleBadArgument("access")
|
||||
)
|
||||
return (path, options) => {
|
||||
let mode = NFS.constants.F_OK
|
||||
if (options?.readable) {
|
||||
mode |= NFS.constants.R_OK
|
||||
}
|
||||
if (options?.writable) {
|
||||
mode |= NFS.constants.W_OK
|
||||
}
|
||||
return nodeAccess(path, mode)
|
||||
}
|
||||
})()
|
||||
|
||||
// == copy
|
||||
|
||||
const copy = ((): FileSystem.FileSystem["copy"] => {
|
||||
const nodeCp = effectify(
|
||||
NFS.cp,
|
||||
handleErrnoException("FileSystem", "copy"),
|
||||
handleBadArgument("copy")
|
||||
)
|
||||
return (fromPath, toPath, options) =>
|
||||
nodeCp(fromPath, toPath, {
|
||||
force: options?.overwrite ?? false,
|
||||
preserveTimestamps: options?.preserveTimestamps ?? false,
|
||||
recursive: true
|
||||
})
|
||||
})()
|
||||
|
||||
// == copyFile
|
||||
|
||||
const copyFile = (() => {
|
||||
const nodeCopyFile = effectify(
|
||||
NFS.copyFile,
|
||||
handleErrnoException("FileSystem", "copyFile"),
|
||||
handleBadArgument("copyFile")
|
||||
)
|
||||
return (fromPath: string, toPath: string) => nodeCopyFile(fromPath, toPath)
|
||||
})()
|
||||
|
||||
// == chmod
|
||||
|
||||
const chmod = (() => {
|
||||
const nodeChmod = effectify(
|
||||
NFS.chmod,
|
||||
handleErrnoException("FileSystem", "chmod"),
|
||||
handleBadArgument("chmod")
|
||||
)
|
||||
return (path: string, mode: number) => nodeChmod(path, mode)
|
||||
})()
|
||||
|
||||
// == chown
|
||||
|
||||
const chown = (() => {
|
||||
const nodeChown = effectify(
|
||||
NFS.chown,
|
||||
handleErrnoException("FileSystem", "chown"),
|
||||
handleBadArgument("chown")
|
||||
)
|
||||
return (path: string, uid: number, gid: number) => nodeChown(path, uid, gid)
|
||||
})()
|
||||
|
||||
// == glob
|
||||
|
||||
const glob = ((): FileSystem.FileSystem["glob"] => {
|
||||
const nodeGlob = effectify(
|
||||
NFS.glob,
|
||||
handleErrnoException("FileSystem", "glob"),
|
||||
handleBadArgument("glob")
|
||||
)
|
||||
return (pattern: string, options) =>
|
||||
nodeGlob(pattern, {
|
||||
cwd: options?.root,
|
||||
exclude: options?.exclude
|
||||
})
|
||||
})()
|
||||
|
||||
// == link
|
||||
|
||||
const link = (() => {
|
||||
const nodeLink = effectify(
|
||||
NFS.link,
|
||||
handleErrnoException("FileSystem", "link"),
|
||||
handleBadArgument("link")
|
||||
)
|
||||
return (existingPath: string, newPath: string) => nodeLink(existingPath, newPath)
|
||||
})()
|
||||
|
||||
// == makeDirectory
|
||||
|
||||
const makeDirectory = ((): FileSystem.FileSystem["makeDirectory"] => {
|
||||
const nodeMkdir = effectify(
|
||||
NFS.mkdir,
|
||||
handleErrnoException("FileSystem", "makeDirectory"),
|
||||
handleBadArgument("makeDirectory")
|
||||
)
|
||||
return (path, options) =>
|
||||
nodeMkdir(path, {
|
||||
recursive: options?.recursive ?? false,
|
||||
mode: options?.mode
|
||||
})
|
||||
})()
|
||||
|
||||
// == makeTempDirectory
|
||||
|
||||
const makeTempDirectoryFactory = (method: string): FileSystem.FileSystem["makeTempDirectory"] => {
|
||||
const nodeMkdtemp = effectify(
|
||||
NFS.mkdtemp,
|
||||
handleErrnoException("FileSystem", method),
|
||||
handleBadArgument(method)
|
||||
)
|
||||
return (options) =>
|
||||
Effect.suspend(() => {
|
||||
const prefix = options?.prefix ?? ""
|
||||
const directory = typeof options?.directory === "string"
|
||||
? Path.join(options.directory, ".")
|
||||
: OS.tmpdir()
|
||||
|
||||
return nodeMkdtemp(prefix ? Path.join(directory, prefix) : directory + "/")
|
||||
})
|
||||
}
|
||||
const makeTempDirectory = makeTempDirectoryFactory("makeTempDirectory")
|
||||
|
||||
// == remove
|
||||
|
||||
const removeFactory = (method: string): FileSystem.FileSystem["remove"] => {
|
||||
const nodeRm = effectify(
|
||||
NFS.rm,
|
||||
handleErrnoException("FileSystem", method),
|
||||
handleBadArgument(method)
|
||||
)
|
||||
return (path, options) =>
|
||||
nodeRm(
|
||||
path,
|
||||
{ recursive: options?.recursive ?? false, force: options?.force ?? false }
|
||||
)
|
||||
}
|
||||
const remove = removeFactory("remove")
|
||||
|
||||
// == makeTempDirectoryScoped
|
||||
|
||||
const makeTempDirectoryScoped = ((): FileSystem.FileSystem["makeTempDirectoryScoped"] => {
|
||||
const makeDirectory = makeTempDirectoryFactory("makeTempDirectoryScoped")
|
||||
const removeDirectory = removeFactory("makeTempDirectoryScoped")
|
||||
return (options) =>
|
||||
Effect.acquireRelease(
|
||||
makeDirectory(options),
|
||||
(directory) => Effect.orDie(removeDirectory(directory, { recursive: true }))
|
||||
)
|
||||
})()
|
||||
|
||||
// == open
|
||||
|
||||
const openFactory = (method: string): FileSystem.FileSystem["open"] => {
|
||||
const nodeOpen = effectify(
|
||||
NFS.open,
|
||||
handleErrnoException("FileSystem", method),
|
||||
handleBadArgument(method)
|
||||
)
|
||||
const nodeClose = effectify(
|
||||
NFS.close,
|
||||
handleErrnoException("FileSystem", method),
|
||||
handleBadArgument(method)
|
||||
)
|
||||
|
||||
return (path, options) =>
|
||||
pipe(
|
||||
Effect.acquireRelease(
|
||||
nodeOpen(path, options?.flag ?? "r", options?.mode),
|
||||
(fd) => Effect.orDie(nodeClose(fd))
|
||||
),
|
||||
Effect.map((fd) => makeFile(FileSystem.FileDescriptor(fd), options?.flag?.startsWith("a") ?? false))
|
||||
)
|
||||
}
|
||||
const open = openFactory("open")
|
||||
|
||||
const makeFile = (() => {
|
||||
const nodeReadFactory = (method: string) =>
|
||||
effectify(
|
||||
NFS.read,
|
||||
handleErrnoException("FileSystem", method),
|
||||
handleBadArgument(method)
|
||||
)
|
||||
const nodeRead = nodeReadFactory("read")
|
||||
const nodeReadAlloc = nodeReadFactory("readAlloc")
|
||||
const nodeStat = effectify(
|
||||
NFS.fstat,
|
||||
handleErrnoException("FileSystem", "stat"),
|
||||
handleBadArgument("stat")
|
||||
)
|
||||
const nodeTruncate = effectify(
|
||||
NFS.ftruncate,
|
||||
handleErrnoException("FileSystem", "truncate"),
|
||||
handleBadArgument("truncate")
|
||||
)
|
||||
|
||||
const nodeSync = effectify(
|
||||
NFS.fsync,
|
||||
handleErrnoException("FileSystem", "sync"),
|
||||
handleBadArgument("sync")
|
||||
)
|
||||
|
||||
const nodeWriteFactory = (method: string) =>
|
||||
effectify(
|
||||
NFS.write,
|
||||
handleErrnoException("FileSystem", method),
|
||||
handleBadArgument(method)
|
||||
)
|
||||
const nodeWrite = nodeWriteFactory("write")
|
||||
const nodeWriteAll = nodeWriteFactory("writeAll")
|
||||
|
||||
class FileImpl implements FileSystem.File {
|
||||
readonly [FileSystem.FileTypeId]: typeof FileSystem.FileTypeId
|
||||
readonly fd: FileSystem.File.Descriptor
|
||||
private readonly append: boolean
|
||||
|
||||
private position: bigint = BigInt(0)
|
||||
|
||||
constructor(
|
||||
fd: FileSystem.File.Descriptor,
|
||||
append: boolean
|
||||
) {
|
||||
this[FileSystem.FileTypeId] = FileSystem.FileTypeId
|
||||
this.fd = fd
|
||||
this.append = append
|
||||
}
|
||||
|
||||
get stat() {
|
||||
return Effect.map(nodeStat(this.fd), makeFileInfo)
|
||||
}
|
||||
|
||||
get sync() {
|
||||
return nodeSync(this.fd)
|
||||
}
|
||||
|
||||
seek(offset: FileSystem.SizeInput, from: FileSystem.SeekMode) {
|
||||
const offsetSize = FileSystem.Size(offset)
|
||||
return Effect.sync(() => {
|
||||
if (from === "start") {
|
||||
this.position = offsetSize
|
||||
} else if (from === "current") {
|
||||
this.position = this.position + offsetSize
|
||||
}
|
||||
|
||||
return this.position
|
||||
})
|
||||
}
|
||||
|
||||
read(buffer: Uint8Array) {
|
||||
return Effect.suspend(() => {
|
||||
const position = this.position
|
||||
return Effect.map(
|
||||
nodeRead(this.fd, { buffer, position }),
|
||||
(bytesRead) => {
|
||||
const sizeRead = FileSystem.Size(bytesRead)
|
||||
this.position = position + sizeRead
|
||||
return sizeRead
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
readAlloc(size: FileSystem.SizeInput) {
|
||||
const sizeNumber = Number(size)
|
||||
return Effect.suspend(() => {
|
||||
const buffer = Buffer.allocUnsafeSlow(sizeNumber)
|
||||
const position = this.position
|
||||
return Effect.map(
|
||||
nodeReadAlloc(this.fd, { buffer, position }),
|
||||
(bytesRead): Option.Option<Buffer> => {
|
||||
if (bytesRead === 0) {
|
||||
return Option.none()
|
||||
}
|
||||
|
||||
this.position = position + BigInt(bytesRead)
|
||||
if (bytesRead === sizeNumber) {
|
||||
return Option.some(buffer)
|
||||
}
|
||||
|
||||
const dst = Buffer.allocUnsafeSlow(bytesRead)
|
||||
buffer.copy(dst, 0, 0, bytesRead)
|
||||
return Option.some(dst)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
truncate(length?: FileSystem.SizeInput) {
|
||||
return Effect.map(nodeTruncate(this.fd, length ? Number(length) : undefined), () => {
|
||||
if (!this.append) {
|
||||
const len = BigInt(length ?? 0)
|
||||
if (this.position > len) {
|
||||
this.position = len
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
write(buffer: Uint8Array) {
|
||||
return Effect.suspend(() => {
|
||||
const position = this.position
|
||||
return Effect.map(
|
||||
nodeWrite(this.fd, buffer, undefined, undefined, this.append ? undefined : Number(position)),
|
||||
(bytesWritten) => {
|
||||
const sizeWritten = FileSystem.Size(bytesWritten)
|
||||
if (!this.append) {
|
||||
this.position = position + sizeWritten
|
||||
}
|
||||
return sizeWritten
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
private writeAllChunk(buffer: Uint8Array): Effect.Effect<void, Error.PlatformError> {
|
||||
return Effect.suspend(() => {
|
||||
const position = this.position
|
||||
return Effect.flatMap(
|
||||
nodeWriteAll(this.fd, buffer, undefined, undefined, this.append ? undefined : Number(position)),
|
||||
(bytesWritten) => {
|
||||
if (bytesWritten === 0) {
|
||||
return Effect.fail(
|
||||
Error.systemError({
|
||||
module: "FileSystem",
|
||||
method: "writeAll",
|
||||
_tag: "WriteZero",
|
||||
pathOrDescriptor: this.fd,
|
||||
description: "write returned 0 bytes written"
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (!this.append) {
|
||||
this.position = position + BigInt(bytesWritten)
|
||||
}
|
||||
|
||||
return bytesWritten < buffer.length ? this.writeAllChunk(buffer.subarray(bytesWritten)) : Effect.void
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
writeAll(buffer: Uint8Array) {
|
||||
return this.writeAllChunk(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
return (fd: FileSystem.File.Descriptor, append: boolean): FileSystem.File => new FileImpl(fd, append)
|
||||
})()
|
||||
|
||||
// == makeTempFile
|
||||
|
||||
const makeTempFileFactory = (method: string): FileSystem.FileSystem["makeTempFile"] => {
|
||||
const makeDirectory = makeTempDirectoryFactory(method)
|
||||
return Effect.fnUntraced(function*(options) {
|
||||
const directory = yield* makeDirectory(options)
|
||||
const random = Crypto.randomBytes(6).toString("hex")
|
||||
const name = Path.join(directory, options?.suffix ? `${random}${options.suffix}` : random)
|
||||
yield* writeFile(name, new Uint8Array(0))
|
||||
return name
|
||||
})
|
||||
}
|
||||
const makeTempFile = makeTempFileFactory("makeTempFile")
|
||||
|
||||
// == makeTempFileScoped
|
||||
|
||||
const makeTempFileScoped = ((): FileSystem.FileSystem["makeTempFileScoped"] => {
|
||||
const makeFile = makeTempFileFactory("makeTempFileScoped")
|
||||
const removeDirectory = removeFactory("makeTempFileScoped")
|
||||
return (options) =>
|
||||
Effect.acquireRelease(
|
||||
makeFile(options),
|
||||
(file) => Effect.orDie(removeDirectory(Path.dirname(file), { recursive: true }))
|
||||
)
|
||||
})()
|
||||
|
||||
// == readDirectory
|
||||
|
||||
const readDirectory: FileSystem.FileSystem["readDirectory"] = (path, options) =>
|
||||
Effect.tryPromise({
|
||||
try: () => NFS.promises.readdir(path, options),
|
||||
catch: (err) => handleErrnoException("FileSystem", "readDirectory")(err as any, [path])
|
||||
})
|
||||
|
||||
// == readFile
|
||||
|
||||
const readFile = (path: string) =>
|
||||
Effect.callback<Uint8Array, Error.PlatformError>((resume, signal) => {
|
||||
try {
|
||||
NFS.readFile(path, { signal }, (err, data) => {
|
||||
if (err) {
|
||||
resume(Effect.fail(handleErrnoException("FileSystem", "readFile")(err, [path])))
|
||||
} else {
|
||||
resume(Effect.succeed(data))
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
resume(Effect.fail(handleBadArgument("readFile")(err)))
|
||||
}
|
||||
})
|
||||
|
||||
// == readLink
|
||||
|
||||
const readLink = (() => {
|
||||
const nodeReadLink = effectify(
|
||||
NFS.readlink,
|
||||
handleErrnoException("FileSystem", "readLink"),
|
||||
handleBadArgument("readLink")
|
||||
)
|
||||
return (path: string) => nodeReadLink(path)
|
||||
})()
|
||||
|
||||
// == realPath
|
||||
|
||||
const realPath = (() => {
|
||||
const nodeRealPath = effectify(
|
||||
NFS.realpath,
|
||||
handleErrnoException("FileSystem", "realPath"),
|
||||
handleBadArgument("realPath")
|
||||
)
|
||||
return (path: string) => nodeRealPath(path)
|
||||
})()
|
||||
|
||||
// == rename
|
||||
|
||||
const rename = (() => {
|
||||
const nodeRename = effectify(
|
||||
NFS.rename,
|
||||
handleErrnoException("FileSystem", "rename"),
|
||||
handleBadArgument("rename")
|
||||
)
|
||||
return (oldPath: string, newPath: string) => nodeRename(oldPath, newPath)
|
||||
})()
|
||||
|
||||
// == stat
|
||||
|
||||
const makeFileInfo = (stat: NFS.Stats): FileSystem.File.Info => ({
|
||||
type: stat.isFile() ?
|
||||
"File" :
|
||||
stat.isDirectory() ?
|
||||
"Directory" :
|
||||
stat.isSymbolicLink() ?
|
||||
"SymbolicLink" :
|
||||
stat.isBlockDevice() ?
|
||||
"BlockDevice" :
|
||||
stat.isCharacterDevice() ?
|
||||
"CharacterDevice" :
|
||||
stat.isFIFO() ?
|
||||
"FIFO" :
|
||||
stat.isSocket() ?
|
||||
"Socket" :
|
||||
"Unknown",
|
||||
mtime: Option.fromNullishOr(stat.mtime),
|
||||
atime: Option.fromNullishOr(stat.atime),
|
||||
birthtime: Option.fromNullishOr(stat.birthtime),
|
||||
dev: stat.dev,
|
||||
rdev: Option.fromNullishOr(stat.rdev),
|
||||
ino: Option.fromNullishOr(stat.ino),
|
||||
mode: stat.mode,
|
||||
nlink: Option.fromNullishOr(stat.nlink),
|
||||
uid: Option.fromNullishOr(stat.uid),
|
||||
gid: Option.fromNullishOr(stat.gid),
|
||||
size: FileSystem.Size(stat.size),
|
||||
blksize: stat.blksize !== undefined ? Option.some(FileSystem.Size(stat.blksize)) : Option.none(),
|
||||
blocks: Option.fromNullishOr(stat.blocks)
|
||||
})
|
||||
const stat = (() => {
|
||||
const nodeStat = effectify(
|
||||
NFS.stat,
|
||||
handleErrnoException("FileSystem", "stat"),
|
||||
handleBadArgument("stat")
|
||||
)
|
||||
return (path: string) => Effect.map(nodeStat(path), makeFileInfo)
|
||||
})()
|
||||
|
||||
// == symlink
|
||||
|
||||
const symlink = (() => {
|
||||
const nodeSymlink = effectify(
|
||||
NFS.symlink,
|
||||
handleErrnoException("FileSystem", "symlink"),
|
||||
handleBadArgument("symlink")
|
||||
)
|
||||
return (target: string, path: string) => nodeSymlink(target, path)
|
||||
})()
|
||||
|
||||
// == truncate
|
||||
|
||||
const truncate = (() => {
|
||||
const nodeTruncate = effectify(
|
||||
NFS.truncate,
|
||||
handleErrnoException("FileSystem", "truncate"),
|
||||
handleBadArgument("truncate")
|
||||
)
|
||||
return (path: string, length?: FileSystem.SizeInput) =>
|
||||
nodeTruncate(path, length !== undefined ? Number(length) : undefined)
|
||||
})()
|
||||
|
||||
// == utimes
|
||||
|
||||
const utimes = (() => {
|
||||
const nodeUtimes = effectify(
|
||||
NFS.utimes,
|
||||
handleErrnoException("FileSystem", "utime"),
|
||||
handleBadArgument("utime")
|
||||
)
|
||||
return (path: string, atime: number | Date, mtime: number | Date) => nodeUtimes(path, atime, mtime)
|
||||
})()
|
||||
|
||||
// == watch
|
||||
|
||||
const watchNode = (path: string) =>
|
||||
Stream.callback<FileSystem.WatchEvent, Error.PlatformError>((queue) =>
|
||||
Effect.acquireRelease(
|
||||
Effect.sync(() => {
|
||||
const watcher = NFS.watch(path, {
|
||||
recursive: true
|
||||
}, (event, path) => {
|
||||
if (!path) return
|
||||
switch (event) {
|
||||
case "rename": {
|
||||
Effect.runFork(Effect.matchEffect(stat(path), {
|
||||
onSuccess: (_) => Queue.offer(queue, { _tag: "Create", path }),
|
||||
onFailure: (_) => Queue.offer(queue, { _tag: "Remove", path })
|
||||
}))
|
||||
return
|
||||
}
|
||||
case "change": {
|
||||
Queue.offerUnsafe(queue, { _tag: "Update", path })
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
watcher.on("error", (error) => {
|
||||
Queue.failCauseUnsafe(
|
||||
queue,
|
||||
Cause.fail(
|
||||
Error.systemError({
|
||||
module: "FileSystem",
|
||||
_tag: "Unknown",
|
||||
method: "watch",
|
||||
pathOrDescriptor: path,
|
||||
cause: error
|
||||
})
|
||||
)
|
||||
)
|
||||
})
|
||||
watcher.on("close", () => {
|
||||
Queue.endUnsafe(queue)
|
||||
})
|
||||
return watcher
|
||||
}),
|
||||
(watcher) => Effect.sync(() => watcher.close())
|
||||
)
|
||||
)
|
||||
|
||||
const watch = (backend: Option.Option<FileSystem.WatchBackend["Service"]>, path: string) =>
|
||||
stat(path).pipe(
|
||||
Effect.map((stat) =>
|
||||
backend.pipe(
|
||||
Option.flatMap((_) => _.register(path, stat)),
|
||||
Option.getOrElse(() => watchNode(path))
|
||||
)
|
||||
),
|
||||
Stream.unwrap
|
||||
)
|
||||
|
||||
// == writeFile
|
||||
|
||||
const writeFile: FileSystem.FileSystem["writeFile"] = (path, data, options) =>
|
||||
Effect.callback<void, Error.PlatformError>((resume, signal) => {
|
||||
try {
|
||||
NFS.writeFile(path, data, {
|
||||
signal,
|
||||
flag: options?.flag,
|
||||
mode: options?.mode
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
resume(Effect.fail(handleErrnoException("FileSystem", "writeFile")(err, [path])))
|
||||
} else {
|
||||
resume(Effect.void)
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
resume(Effect.fail(handleBadArgument("writeFile")(err)))
|
||||
}
|
||||
})
|
||||
|
||||
const makeFileSystem = Effect.map(Effect.serviceOption(FileSystem.WatchBackend), (backend) =>
|
||||
FileSystem.make({
|
||||
access,
|
||||
chmod,
|
||||
chown,
|
||||
copy,
|
||||
copyFile,
|
||||
glob,
|
||||
link,
|
||||
makeDirectory,
|
||||
makeTempDirectory,
|
||||
makeTempDirectoryScoped,
|
||||
makeTempFile,
|
||||
makeTempFileScoped,
|
||||
open,
|
||||
readDirectory,
|
||||
readFile,
|
||||
readLink,
|
||||
realPath,
|
||||
remove,
|
||||
rename,
|
||||
stat,
|
||||
symlink,
|
||||
truncate,
|
||||
utimes,
|
||||
watch(path) {
|
||||
return watch(backend, path)
|
||||
},
|
||||
writeFile
|
||||
}))
|
||||
|
||||
/**
|
||||
* Provides the `FileSystem` service backed by Node filesystem APIs, including
|
||||
* file operations, directory operations, links, metadata, and file watching.
|
||||
*
|
||||
* @category layers
|
||||
* @since 4.0.0
|
||||
*/
|
||||
export const layer: Layer.Layer<FileSystem.FileSystem> = Layer.effect(FileSystem.FileSystem)(makeFileSystem)
|
||||
Reference in New Issue
Block a user