nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
1,440 lines (1,274 loc) • 36.6 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/fs/promises.js
import * as __hoisted_internal_fs_streams__ from "nstdlib/lib/internal/fs/streams";
import * as __hoisted_internal_fs_rimraf__ from "nstdlib/lib/internal/fs/rimraf";
import * as __hoisted_internal_webstreams_adapters__ from "nstdlib/lib/internal/webstreams/adapters";
import * as __hoisted_internal_webstreams_readablestream__ from "nstdlib/lib/internal/webstreams/readablestream";
import * as __hoisted_internal_fs_glob__ from "nstdlib/lib/internal/fs/glob";
import { fs as constants } from "nstdlib/stub/binding/constants";
import * as binding from "nstdlib/stub/binding/fs";
import { Buffer } from "nstdlib/lib/buffer";
import {
AbortError,
aggregateTwoErrors,
codes as __codes__,
} from "nstdlib/lib/internal/errors";
import { isArrayBufferView } from "nstdlib/lib/internal/util/types";
import {
constants as __constants__,
copyObject,
emitRecursiveRmdirWarning,
getDirents,
getOptions,
getStatFsFromBinding,
getStatsFromBinding,
getValidatedPath,
preprocessSymlinkDestination,
stringToFlags,
stringToSymlinkType,
toUnixTimestamp,
validateBufferArray,
validateCpOptions,
validateOffsetLengthRead,
validateOffsetLengthWrite,
validatePosition,
validateRmOptions,
validateRmdirOptions,
validateStringAfterArrayBufferView,
warnOnNonPortableTemplate,
} from "nstdlib/lib/internal/fs/utils";
import { opendir } from "nstdlib/lib/internal/fs/dir";
import {
parseFileMode,
validateAbortSignal,
validateBoolean,
validateBuffer,
validateEncoding,
validateInteger,
validateObject,
validateString,
kValidateObjectAllowNullable,
} from "nstdlib/lib/internal/validators";
import * as pathModule from "nstdlib/lib/path";
import { toPathIfFileURL } from "nstdlib/lib/internal/url";
import {
emitExperimentalWarning,
getLazy,
kEmptyObject,
lazyDOMException,
promisify,
isWindows,
isMacOS,
} from "nstdlib/lib/internal/util";
import EventEmitter from "nstdlib/lib/events";
import { StringDecoder } from "nstdlib/lib/string_decoder";
import { kFSWatchStart, watch } from "nstdlib/lib/internal/fs/watchers";
import * as nonNativeWatcher from "nstdlib/lib/internal/fs/recursive_watch";
import { isIterable } from "nstdlib/lib/internal/streams/utils";
import * as assert from "nstdlib/lib/internal/assert";
import * as permission from "nstdlib/lib/internal/process/permission";
import { Interface } from "nstdlib/lib/internal/readline/interface";
import {
kDeserialize,
kTransfer,
kTransferList,
markTransferMode,
} from "nstdlib/lib/internal/worker/js_transferable";
import * as __hoisted_internal_fs_cp_cp__ from "nstdlib/lib/internal/fs/cp/cp";
const { F_OK, O_SYMLINK, O_WRONLY, S_IFMT, S_IFREG } = constants;
const { isBuffer: BufferIsBuffer } = Buffer;
const BufferToString = uncurryThis(Buffer.prototype.toString);
const {
ERR_ACCESS_DENIED,
ERR_FS_FILE_TOO_LARGE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_STATE,
ERR_METHOD_NOT_IMPLEMENTED,
} = __codes__;
const {
kIoMaxLength,
kMaxUserId,
kReadFileBufferLength,
kReadFileUnknownBufferLength,
kWriteFileMaxChunkSize,
} = __constants__;
const { isAbsolute } = pathModule;
const kHandle = Symbol("kHandle");
const kFd = Symbol("kFd");
const kRefs = Symbol("kRefs");
const kClosePromise = Symbol("kClosePromise");
const kCloseResolve = Symbol("kCloseResolve");
const kCloseReject = Symbol("kCloseReject");
const kRef = Symbol("kRef");
const kUnref = Symbol("kUnref");
const kLocked = Symbol("kLocked");
const { kUsePromises } = binding;
const getDirectoryEntriesPromise = promisify(getDirents);
const validateRmOptionsPromise = promisify(validateRmOptions);
let cpPromises;
function lazyLoadCpPromises() {
return (cpPromises ??= __hoisted_internal_fs_cp_cp__.cpFn);
}
// Lazy loaded to avoid circular dependency.
let fsStreams;
function lazyFsStreams() {
return (fsStreams ??= __hoisted_internal_fs_streams__);
}
const lazyRimRaf = getLazy(() => __hoisted_internal_fs_rimraf__.rimrafPromises);
// By the time the C++ land creates an error for a promise rejection (likely from a
// libuv callback), there is already no JS frames on the stack. So we need to
// wait until V8 resumes execution back to JS land before we have enough information
// to re-capture the stack trace.
function handleErrorFromBinding(error) {
Error.captureStackTrace(error, handleErrorFromBinding);
return Promise.reject(error);
}
class FileHandle extends EventEmitter {
/**
* @param {InternalFSBinding.FileHandle | undefined} filehandle
*/
constructor(filehandle) {
super();
markTransferMode(this, false, true);
this[kHandle] = filehandle;
this[kFd] = filehandle ? filehandle.fd : -1;
this[kRefs] = 1;
this[kClosePromise] = null;
}
getAsyncId() {
return this[kHandle].getAsyncId();
}
get fd() {
return this[kFd];
}
appendFile(data, options) {
return fsCall(writeFile, this, data, options);
}
chmod(mode) {
return fsCall(fchmod, this, mode);
}
chown(uid, gid) {
return fsCall(fchown, this, uid, gid);
}
datasync() {
return fsCall(fdatasync, this);
}
sync() {
return fsCall(fsync, this);
}
read(buffer, offset, length, position) {
return fsCall(read, this, buffer, offset, length, position);
}
readv(buffers, position) {
return fsCall(readv, this, buffers, position);
}
readFile(options) {
return fsCall(readFile, this, options);
}
readLines(options = undefined) {
return new Interface({
input: this.createReadStream(options),
crlfDelay: Infinity,
});
}
stat(options) {
return fsCall(fstat, this, options);
}
truncate(len = 0) {
return fsCall(ftruncate, this, len);
}
utimes(atime, mtime) {
return fsCall(futimes, this, atime, mtime);
}
write(buffer, offset, length, position) {
return fsCall(write, this, buffer, offset, length, position);
}
writev(buffers, position) {
return fsCall(writev, this, buffers, position);
}
writeFile(data, options) {
return fsCall(writeFile, this, data, options);
}
close = () => {
if (this[kFd] === -1) {
return Promise.resolve();
}
if (this[kClosePromise]) {
return this[kClosePromise];
}
this[kRefs]--;
if (this[kRefs] === 0) {
this[kFd] = -1;
this[kClosePromise] = Promise.prototype.finally.call(
this[kHandle].close(),
() => {
this[kClosePromise] = undefined;
},
);
} else {
this[kClosePromise] = Promise.prototype.finally.call(
new Promise((resolve, reject) => {
this[kCloseResolve] = resolve;
this[kCloseReject] = reject;
}),
() => {
this[kClosePromise] = undefined;
this[kCloseReject] = undefined;
this[kCloseResolve] = undefined;
},
);
}
this.emit("close");
return this[kClosePromise];
};
async [Symbol.for("nodejs.asyncDispose")]() {
return this.close();
}
/**
* @typedef {import('../webstreams/readablestream').ReadableStream
* } ReadableStream
* @param {{
* type?: string;
* }} [options]
* @returns {ReadableStream}
*/
readableWebStream(options = kEmptyObject) {
if (this[kFd] === -1)
throw new ERR_INVALID_STATE("The FileHandle is closed");
if (this[kClosePromise])
throw new ERR_INVALID_STATE("The FileHandle is closing");
if (this[kLocked]) throw new ERR_INVALID_STATE("The FileHandle is locked");
this[kLocked] = true;
if (options.type !== undefined) {
validateString(options.type, "options.type");
}
let readable;
if (options.type !== "bytes") {
const { newReadableStreamFromStreamBase } =
__hoisted_internal_webstreams_adapters__;
readable = newReadableStreamFromStreamBase(this[kHandle], undefined, {
ondone: () => this[kUnref](),
});
} else {
const { ReadableStream } = __hoisted_internal_webstreams_readablestream__;
const readFn = Function.prototype.bind.call(this.read, this);
const ondone = Function.prototype.bind.call(this[kUnref], this);
readable = new ReadableStream({
type: "bytes",
autoAllocateChunkSize: 16384,
async pull(controller) {
const view = controller.byobRequest.view;
const { bytesRead } = await readFn(
view,
view.byteOffset,
view.byteLength,
);
if (bytesRead === 0) {
ondone();
controller.close();
}
controller.byobRequest.respond(bytesRead);
},
cancel() {
ondone();
},
});
}
const { readableStreamCancel } =
__hoisted_internal_webstreams_readablestream__;
this[kRef]();
this.once("close", () => {
readableStreamCancel(readable);
});
return readable;
}
/**
* @typedef {import('./streams').ReadStream
* } ReadStream
* @param {{
* encoding?: string;
* autoClose?: boolean;
* emitClose?: boolean;
* start: number;
* end?: number;
* highWaterMark?: number;
* }} [options]
* @returns {ReadStream}
*/
createReadStream(options = undefined) {
const { ReadStream } = lazyFsStreams();
return new ReadStream(undefined, { ...options, fd: this });
}
/**
* @typedef {import('./streams').WriteStream
* } WriteStream
* @param {{
* encoding?: string;
* autoClose?: boolean;
* emitClose?: boolean;
* start: number;
* highWaterMark?: number;
* flush?: boolean;
* }} [options]
* @returns {WriteStream}
*/
createWriteStream(options = undefined) {
const { WriteStream } = lazyFsStreams();
return new WriteStream(undefined, { ...options, fd: this });
}
[kTransfer]() {
if (this[kClosePromise] || this[kRefs] > 1) {
throw lazyDOMException(
"Cannot transfer FileHandle while in use",
"DataCloneError",
);
}
const handle = this[kHandle];
this[kFd] = -1;
this[kHandle] = null;
this[kRefs] = 0;
return {
data: { handle },
deserializeInfo: "internal/fs/promises:FileHandle",
};
}
[kTransferList]() {
return [this[kHandle]];
}
[kDeserialize]({ handle }) {
this[kHandle] = handle;
this[kFd] = handle.fd;
}
[kRef]() {
this[kRefs]++;
}
[kUnref]() {
this[kRefs]--;
if (this[kRefs] === 0) {
this[kFd] = -1;
Promise.prototype.then.call(
this[kHandle].close(),
this[kCloseResolve],
this[kCloseReject],
);
}
}
}
async function handleFdClose(fileOpPromise, closeFunc) {
return Promise.prototype.then.call(
fileOpPromise,
(result) => Promise.prototype.then.call(closeFunc(), () => result),
(opError) =>
Promise.prototype.then.call(
closeFunc(),
() => Promise.reject(opError),
(closeError) => Promise.reject(aggregateTwoErrors(closeError, opError)),
),
);
}
async function handleFdSync(fileOpPromise, handle) {
return Promise.prototype.then.call(
fileOpPromise,
(result) =>
Promise.prototype.then.call(
handle.sync(),
() => result,
(syncError) => Promise.reject(syncError),
),
(opError) => Promise.reject(opError),
);
}
async function fsCall(fn, handle, ...args) {
assert(
handle[kRefs] !== undefined,
"handle must be an instance of FileHandle",
);
if (handle.fd === -1) {
// eslint-disable-next-line no-restricted-syntax
const err = new Error("file closed");
err.code = "EBADF";
err.syscall = fn.name;
throw err;
}
try {
handle[kRef]();
return await fn(handle, ...new (Array.prototype[Symbol.iterator]())(args));
} finally {
handle[kUnref]();
}
}
function checkAborted(signal) {
if (signal?.aborted)
throw new AbortError(undefined, { cause: signal?.reason });
}
async function writeFileHandle(filehandle, data, signal, encoding) {
checkAborted(signal);
if (isCustomIterable(data)) {
for await (const buf of data) {
checkAborted(signal);
const toWrite = isArrayBufferView(buf)
? buf
: Buffer.from(buf, encoding || "utf8");
let remaining = toWrite.byteLength;
while (remaining > 0) {
const writeSize = Math.min(kWriteFileMaxChunkSize, remaining);
const { bytesWritten } = await write(
filehandle,
toWrite,
toWrite.byteLength - remaining,
writeSize,
);
remaining -= bytesWritten;
checkAborted(signal);
}
}
return;
}
data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
let remaining = data.byteLength;
if (remaining === 0) return;
do {
checkAborted(signal);
const { bytesWritten } = await write(
filehandle,
data,
0,
Math.min(kWriteFileMaxChunkSize, data.byteLength),
);
remaining -= bytesWritten;
data = new Uint8Array(
data.buffer,
data.byteOffset + bytesWritten,
data.byteLength - bytesWritten,
);
} while (remaining > 0);
}
async function readFileHandle(filehandle, options) {
const signal = options?.signal;
const encoding = options?.encoding;
const decoder = encoding && new StringDecoder(encoding);
checkAborted(signal);
const statFields = await Promise.prototype.then.call(
binding.fstat(filehandle.fd, false, kUsePromises),
undefined,
handleErrorFromBinding,
);
checkAborted(signal);
let size = 0;
let length = 0;
if ((statFields[1 /* mode */] & S_IFMT) === S_IFREG) {
size = statFields[8 /* size */];
length = encoding ? Math.min(size, kReadFileBufferLength) : size;
}
if (length === 0) {
length = kReadFileUnknownBufferLength;
}
if (size > kIoMaxLength) throw new ERR_FS_FILE_TOO_LARGE(size);
let totalRead = 0;
const noSize = size === 0;
let buffer = Buffer.allocUnsafeSlow(length);
let result = "";
let offset = 0;
let buffers;
const chunkedRead = length > kReadFileBufferLength;
while (true) {
checkAborted(signal);
if (chunkedRead) {
length = Math.min(size - totalRead, kReadFileBufferLength);
}
const bytesRead =
(await Promise.prototype.then.call(
binding.read(filehandle.fd, buffer, offset, length, -1, kUsePromises),
undefined,
handleErrorFromBinding,
)) ?? 0;
totalRead += bytesRead;
if (
bytesRead === 0 ||
totalRead === size ||
(bytesRead !== buffer.length && !chunkedRead && !noSize)
) {
const singleRead = bytesRead === totalRead;
const bytesToCheck = chunkedRead ? totalRead : bytesRead;
if (bytesToCheck !== buffer.length) {
buffer = buffer.subarray(0, bytesToCheck);
}
if (!encoding) {
if (noSize && !singleRead) {
Array.prototype.push.call(buffers, buffer);
return Buffer.concat(buffers, totalRead);
}
return buffer;
}
if (singleRead) {
return buffer.toString(encoding);
}
result += decoder.end(buffer);
return result;
}
const readBuffer =
bytesRead !== buffer.length ? buffer.subarray(0, bytesRead) : buffer;
if (encoding) {
result += decoder.write(readBuffer);
} else if (size !== 0) {
offset = totalRead;
} else {
buffers ??= [];
// Unknown file size requires chunks.
Array.prototype.push.call(buffers, readBuffer);
buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength);
}
}
}
// All of the functions are defined as async in order to ensure that errors
// thrown cause promise rejections rather than being thrown synchronously.
async function access(path, mode = F_OK) {
return await Promise.prototype.then.call(
binding.access(getValidatedPath(path), mode, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function cp(src, dest, options) {
options = validateCpOptions(options);
src = getValidatedPath(src, "src");
dest = getValidatedPath(dest, "dest");
return lazyLoadCpPromises()(src, dest, options);
}
async function copyFile(src, dest, mode) {
return await Promise.prototype.then.call(
binding.copyFile(
getValidatedPath(src, "src"),
getValidatedPath(dest, "dest"),
mode,
kUsePromises,
),
undefined,
handleErrorFromBinding,
);
}
// Note that unlike fs.open() which uses numeric file descriptors,
// fsPromises.open() uses the fs.FileHandle class.
async function open(path, flags, mode) {
path = getValidatedPath(path);
const flagsNumber = stringToFlags(flags);
mode = parseFileMode(mode, "mode", 0o666);
return new FileHandle(
await Promise.prototype.then.call(
binding.openFileHandle(path, flagsNumber, mode, kUsePromises),
undefined,
handleErrorFromBinding,
),
);
}
async function read(handle, bufferOrParams, offset, length, position) {
let buffer = bufferOrParams;
if (!isArrayBufferView(buffer)) {
// This is fh.read(params)
if (bufferOrParams !== undefined) {
validateObject(bufferOrParams, "options", kValidateObjectAllowNullable);
}
({
buffer = Buffer.alloc(16384),
offset = 0,
length = buffer.byteLength - offset,
position = null,
} = bufferOrParams ?? kEmptyObject);
validateBuffer(buffer);
}
if (offset !== null && typeof offset === "object") {
// This is fh.read(buffer, options)
({
offset = 0,
length = buffer.byteLength - offset,
position = null,
} = offset);
}
if (offset == null) {
offset = 0;
} else {
validateInteger(offset, "offset", 0);
}
length ??= buffer.byteLength - offset;
if (length === 0) return { __proto__: null, bytesRead: length, buffer };
if (buffer.byteLength === 0) {
throw new ERR_INVALID_ARG_VALUE(
"buffer",
buffer,
"is empty and cannot be written",
);
}
validateOffsetLengthRead(offset, length, buffer.byteLength);
if (position == null) {
position = -1;
} else {
validatePosition(position, "position", length);
}
const bytesRead =
(await Promise.prototype.then.call(
binding.read(handle.fd, buffer, offset, length, position, kUsePromises),
undefined,
handleErrorFromBinding,
)) || 0;
return { __proto__: null, bytesRead, buffer };
}
async function readv(handle, buffers, position) {
validateBufferArray(buffers);
if (typeof position !== "number") position = null;
const bytesRead =
(await Promise.prototype.then.call(
binding.readBuffers(handle.fd, buffers, position, kUsePromises),
undefined,
handleErrorFromBinding,
)) || 0;
return { __proto__: null, bytesRead, buffers };
}
async function write(handle, buffer, offsetOrOptions, length, position) {
if (buffer?.byteLength === 0)
return { __proto__: null, bytesWritten: 0, buffer };
let offset = offsetOrOptions;
if (isArrayBufferView(buffer)) {
if (typeof offset === "object") {
({
offset = 0,
length = buffer.byteLength - offset,
position = null,
} = offsetOrOptions ?? kEmptyObject);
}
if (offset == null) {
offset = 0;
} else {
validateInteger(offset, "offset", 0);
}
if (typeof length !== "number") length = buffer.byteLength - offset;
if (typeof position !== "number") position = null;
validateOffsetLengthWrite(offset, length, buffer.byteLength);
const bytesWritten =
(await Promise.prototype.then.call(
binding.writeBuffer(
handle.fd,
buffer,
offset,
length,
position,
kUsePromises,
),
undefined,
handleErrorFromBinding,
)) || 0;
return { __proto__: null, bytesWritten, buffer };
}
validateStringAfterArrayBufferView(buffer, "buffer");
validateEncoding(buffer, length);
const bytesWritten =
(await Promise.prototype.then.call(
binding.writeString(handle.fd, buffer, offset, length, kUsePromises),
undefined,
handleErrorFromBinding,
)) || 0;
return { __proto__: null, bytesWritten, buffer };
}
async function writev(handle, buffers, position) {
validateBufferArray(buffers);
if (typeof position !== "number") position = null;
if (buffers.length === 0) {
return { __proto__: null, bytesWritten: 0, buffers };
}
const bytesWritten =
(await Promise.prototype.then.call(
binding.writeBuffers(handle.fd, buffers, position, kUsePromises),
undefined,
handleErrorFromBinding,
)) || 0;
return { __proto__: null, bytesWritten, buffers };
}
async function rename(oldPath, newPath) {
oldPath = getValidatedPath(oldPath, "oldPath");
newPath = getValidatedPath(newPath, "newPath");
return await Promise.prototype.then.call(
binding.rename(oldPath, newPath, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function truncate(path, len = 0) {
const fd = await open(path, "r+");
return handleFdClose(ftruncate(fd, len), fd.close);
}
async function ftruncate(handle, len = 0) {
validateInteger(len, "len");
len = Math.max(0, len);
return await Promise.prototype.then.call(
binding.ftruncate(handle.fd, len, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function rm(path, options) {
path = getValidatedPath(path);
options = await validateRmOptionsPromise(path, options, false);
return lazyRimRaf()(path, options);
}
async function rmdir(path, options) {
path = getValidatedPath(path);
options = validateRmdirOptions(options);
if (options.recursive) {
emitRecursiveRmdirWarning();
const stats = await stat(path);
if (stats.isDirectory()) {
return lazyRimRaf()(path, options);
}
}
return await Promise.prototype.then.call(
binding.rmdir(path, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function fdatasync(handle) {
return await Promise.prototype.then.call(
binding.fdatasync(handle.fd, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function fsync(handle) {
return await Promise.prototype.then.call(
binding.fsync(handle.fd, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function mkdir(path, options) {
if (typeof options === "number" || typeof options === "string") {
options = { mode: options };
}
const { recursive = false, mode = 0o777 } = options || kEmptyObject;
path = getValidatedPath(path);
validateBoolean(recursive, "options.recursive");
return await Promise.prototype.then.call(
binding.mkdir(
path,
parseFileMode(mode, "mode", 0o777),
recursive,
kUsePromises,
),
undefined,
handleErrorFromBinding,
);
}
async function readdirRecursive(originalPath, options) {
const result = [];
const queue = [
[
originalPath,
await Promise.prototype.then.call(
binding.readdir(
originalPath,
options.encoding,
!!options.withFileTypes,
kUsePromises,
),
undefined,
handleErrorFromBinding,
),
],
];
if (options.withFileTypes) {
while (queue.length > 0) {
// If we want to implement BFS make this a `shift` call instead of `pop`
const { 0: path, 1: readdir } = Array.prototype.pop.call(queue);
for (const dirent of getDirents(path, readdir)) {
Array.prototype.push.call(result, dirent);
if (dirent.isDirectory()) {
const direntPath = pathModule.join(path, dirent.name);
Array.prototype.push.call(queue, [
direntPath,
await Promise.prototype.then.call(
binding.readdir(direntPath, options.encoding, true, kUsePromises),
undefined,
handleErrorFromBinding,
),
]);
}
}
}
} else {
while (queue.length > 0) {
const { 0: path, 1: readdir } = Array.prototype.pop.call(queue);
for (const ent of readdir) {
const direntPath = pathModule.join(path, ent);
const stat = binding.internalModuleStat(direntPath);
Array.prototype.push.call(
result,
pathModule.relative(originalPath, direntPath),
);
if (stat === 1) {
Array.prototype.push.call(queue, [
direntPath,
await Promise.prototype.then.call(
binding.readdir(
direntPath,
options.encoding,
false,
kUsePromises,
),
undefined,
handleErrorFromBinding,
),
]);
}
}
}
}
return result;
}
async function readdir(path, options) {
options = getOptions(options);
path = getValidatedPath(path);
if (options.recursive) {
return readdirRecursive(path, options);
}
const result = await Promise.prototype.then.call(
binding.readdir(
path,
options.encoding,
!!options.withFileTypes,
kUsePromises,
),
undefined,
handleErrorFromBinding,
);
return options.withFileTypes
? getDirectoryEntriesPromise(path, result)
: result;
}
async function readlink(path, options) {
options = getOptions(options);
path = getValidatedPath(path, "oldPath");
return await Promise.prototype.then.call(
binding.readlink(path, options.encoding, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function symlink(target, path, type_) {
let type = typeof type_ === "string" ? type_ : null;
if (isWindows && type === null) {
try {
const absoluteTarget = pathModule.resolve(`${path}`, "..", `${target}`);
type = (await stat(absoluteTarget)).isDirectory() ? "dir" : "file";
} catch {
// Default to 'file' if path is invalid or file does not exist
type = "file";
}
}
if (permission.isEnabled()) {
// The permission model's security guarantees fall apart in the presence of
// relative symbolic links. Thus, we have to prevent their creation.
if (BufferIsBuffer(target)) {
if (!isAbsolute(BufferToString(target))) {
throw new ERR_ACCESS_DENIED("relative symbolic link target");
}
} else if (
typeof target !== "string" ||
!isAbsolute(toPathIfFileURL(target))
) {
throw new ERR_ACCESS_DENIED("relative symbolic link target");
}
}
target = getValidatedPath(target, "target");
path = getValidatedPath(path);
return await Promise.prototype.then.call(
binding.symlink(
preprocessSymlinkDestination(target, type, path),
path,
stringToSymlinkType(type),
kUsePromises,
),
undefined,
handleErrorFromBinding,
);
}
async function fstat(handle, options = { bigint: false }) {
const result = await Promise.prototype.then.call(
binding.fstat(handle.fd, options.bigint, kUsePromises),
undefined,
handleErrorFromBinding,
);
return getStatsFromBinding(result);
}
async function lstat(path, options = { bigint: false }) {
const result = await Promise.prototype.then.call(
binding.lstat(getValidatedPath(path), options.bigint, kUsePromises),
undefined,
handleErrorFromBinding,
);
return getStatsFromBinding(result);
}
async function stat(path, options = { bigint: false }) {
const result = await Promise.prototype.then.call(
binding.stat(getValidatedPath(path), options.bigint, kUsePromises),
undefined,
handleErrorFromBinding,
);
return getStatsFromBinding(result);
}
async function statfs(path, options = { bigint: false }) {
const result = await Promise.prototype.then.call(
binding.statfs(path, options.bigint, kUsePromises),
undefined,
handleErrorFromBinding,
);
return getStatFsFromBinding(result);
}
async function link(existingPath, newPath) {
existingPath = getValidatedPath(existingPath, "existingPath");
newPath = getValidatedPath(newPath, "newPath");
return await Promise.prototype.then.call(
binding.link(existingPath, newPath, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function unlink(path) {
return await Promise.prototype.then.call(
binding.unlink(getValidatedPath(path), kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function fchmod(handle, mode) {
mode = parseFileMode(mode, "mode");
return await Promise.prototype.then.call(
binding.fchmod(handle.fd, mode, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function chmod(path, mode) {
path = getValidatedPath(path);
mode = parseFileMode(mode, "mode");
return await Promise.prototype.then.call(
binding.chmod(path, mode, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function lchmod(path, mode) {
if (O_SYMLINK === undefined) throw new ERR_METHOD_NOT_IMPLEMENTED("lchmod()");
const fd = await open(path, O_WRONLY | O_SYMLINK);
return handleFdClose(fchmod(fd, mode), fd.close);
}
async function lchown(path, uid, gid) {
path = getValidatedPath(path);
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
return await Promise.prototype.then.call(
binding.lchown(path, uid, gid, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function fchown(handle, uid, gid) {
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
return await Promise.prototype.then.call(
binding.fchown(handle.fd, uid, gid, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function chown(path, uid, gid) {
path = getValidatedPath(path);
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
return await Promise.prototype.then.call(
binding.chown(path, uid, gid, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function utimes(path, atime, mtime) {
path = getValidatedPath(path);
return await Promise.prototype.then.call(
binding.utimes(
path,
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
kUsePromises,
),
undefined,
handleErrorFromBinding,
);
}
async function futimes(handle, atime, mtime) {
atime = toUnixTimestamp(atime, "atime");
mtime = toUnixTimestamp(mtime, "mtime");
return await Promise.prototype.then.call(
binding.futimes(handle.fd, atime, mtime, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function lutimes(path, atime, mtime) {
return await Promise.prototype.then.call(
binding.lutimes(
getValidatedPath(path),
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
kUsePromises,
),
undefined,
handleErrorFromBinding,
);
}
async function realpath(path, options) {
options = getOptions(options);
return await Promise.prototype.then.call(
binding.realpath(getValidatedPath(path), options.encoding, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function mkdtemp(prefix, options) {
options = getOptions(options);
prefix = getValidatedPath(prefix, "prefix");
warnOnNonPortableTemplate(prefix);
return await Promise.prototype.then.call(
binding.mkdtemp(prefix, options.encoding, kUsePromises),
undefined,
handleErrorFromBinding,
);
}
async function writeFile(path, data, options) {
options = getOptions(options, {
encoding: "utf8",
mode: 0o666,
flag: "w",
flush: false,
});
const flag = options.flag || "w";
const flush = options.flush ?? false;
validateBoolean(flush, "options.flush");
if (!isArrayBufferView(data) && !isCustomIterable(data)) {
validateStringAfterArrayBufferView(data, "data");
data = Buffer.from(data, options.encoding || "utf8");
}
validateAbortSignal(options.signal);
if (path instanceof FileHandle)
return writeFileHandle(path, data, options.signal, options.encoding);
checkAborted(options.signal);
const fd = await open(path, flag, options.mode);
let writeOp = writeFileHandle(fd, data, options.signal, options.encoding);
if (flush) {
writeOp = handleFdSync(writeOp, fd);
}
return handleFdClose(writeOp, fd.close);
}
function isCustomIterable(obj) {
return isIterable(obj) && !isArrayBufferView(obj) && typeof obj !== "string";
}
async function appendFile(path, data, options) {
options = getOptions(options, { encoding: "utf8", mode: 0o666, flag: "a" });
options = copyObject(options);
options.flag = options.flag || "a";
return writeFile(path, data, options);
}
async function readFile(path, options) {
options = getOptions(options, { flag: "r" });
const flag = options.flag || "r";
if (path instanceof FileHandle) return readFileHandle(path, options);
checkAborted(options.signal);
const fd = await open(path, flag, 0o666);
return handleFdClose(readFileHandle(fd, options), fd.close);
}
async function* _watch(filename, options = kEmptyObject) {
validateObject(options, "options");
if (options.recursive != null) {
validateBoolean(options.recursive, "options.recursive");
// TODO(anonrig): Remove non-native watcher when/if libuv supports recursive.
// As of November 2022, libuv does not support recursive file watch on all platforms,
// e.g. Linux due to the limitations of inotify.
if (options.recursive && !isMacOS && !isWindows) {
const watcher = new nonNativeWatcher.FSWatcher(options);
watcher[kFSWatchStart](filename);
yield* watcher;
return;
}
}
yield* watch(filename, options);
}
const lazyGlob = getLazy(() => __hoisted_internal_fs_glob__.Glob);
async function* glob(pattern, options) {
emitExperimentalWarning("glob");
const Glob = lazyGlob();
yield* new Glob(pattern, options).glob();
}
const _export_exports_ = {
access,
copyFile,
cp,
glob,
open,
opendir: promisify(opendir),
rename,
truncate,
rm,
rmdir,
mkdir,
readdir,
readlink,
symlink,
lstat,
stat,
statfs,
link,
unlink,
chmod,
lchmod,
lchown,
chown,
utimes,
lutimes,
realpath,
mkdtemp,
writeFile,
appendFile,
readFile,
watch: !isMacOS && !isWindows ? _watch : watch,
constants,
};
export { _export_exports_ as exports };
export { FileHandle };
export { kRef };
export { kUnref };
export { access };
export { copyFile };
export { cp };
export { glob };
export { open };
export { rename };
export { truncate };
export { rm };
export { rmdir };
export { mkdir };
export { readdir };
export { readlink };
export { symlink };
export { lstat };
export { stat };
export { statfs };
export { link };
export { unlink };
export { chmod };
export { lchmod };
export { lchown };
export { chown };
export { utimes };
export { lutimes };
export { realpath };
export { mkdtemp };
export { writeFile };
export { appendFile };
export { readFile };
export default {
access,
copyFile,
cp,
glob,
open,
rename,
truncate,
rm,
rmdir,
mkdir,
readdir,
readlink,
symlink,
lstat,
stat,
statfs,
link,
unlink,
chmod,
lchmod,
lchown,
chown,
utimes,
lutimes,
realpath,
mkdtemp,
writeFile,
appendFile,
readFile,
__hoisted_internal_fs_cp_cp__,
__hoisted_internal_fs_streams__,
__hoisted_internal_fs_rimraf__,
__hoisted_internal_webstreams_adapters__,
__hoisted_internal_webstreams_readablestream__,
__hoisted_internal_fs_glob__,
constants,
binding,
Buffer,
AbortError,
aggregateTwoErrors,
__codes__,
isArrayBufferView,
__constants__,
copyObject,
emitRecursiveRmdirWarning,
getDirents,
getOptions,
getStatFsFromBinding,
getStatsFromBinding,
getValidatedPath,
preprocessSymlinkDestination,
stringToFlags,
stringToSymlinkType,
toUnixTimestamp,
validateBufferArray,
validateCpOptions,
validateOffsetLengthRead,
validateOffsetLengthWrite,
validatePosition,
validateRmOptions,
validateRmdirOptions,
validateStringAfterArrayBufferView,
warnOnNonPortableTemplate,
opendir,
parseFileMode,
validateAbortSignal,
validateBoolean,
validateBuffer,
validateEncoding,
validateInteger,
validateObject,
validateString,
kValidateObjectAllowNullable,
pathModule,
toPathIfFileURL,
emitExperimentalWarning,
getLazy,
kEmptyObject,
lazyDOMException,
promisify,
isWindows,
isMacOS,
EventEmitter,
StringDecoder,
kFSWatchStart,
watch,
nonNativeWatcher,
isIterable,
assert,
permission,
Interface,
kDeserialize,
kTransfer,
kTransferList,
markTransferMode,
exports,
FileHandle,
kRef,
kUnref,
};