mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Compare commits
7 Commits
claude/fix
...
codex/move
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
261826b80f | ||
|
|
e29c629eb9 | ||
|
|
ec1696f5fa | ||
|
|
4cb2e33e28 | ||
|
|
6748419e75 | ||
|
|
6ca072c414 | ||
|
|
baf975076a |
@@ -54,6 +54,7 @@ src/js/internal/fifo.ts
|
||||
src/js/internal/fixed_queue.ts
|
||||
src/js/internal/fs/cp-sync.ts
|
||||
src/js/internal/fs/cp.ts
|
||||
src/js/internal/fs/FileHandle.ts
|
||||
src/js/internal/fs/glob.ts
|
||||
src/js/internal/fs/streams.ts
|
||||
src/js/internal/html.ts
|
||||
|
||||
@@ -159,8 +159,6 @@ export const bindgen = $zig("bindgen_test.zig", "getBindgenTestFunctions") as {
|
||||
export const noOpForTesting = $cpp("NoOpForTesting.cpp", "createNoOpForTesting");
|
||||
export const Dequeue = require("internal/fifo");
|
||||
|
||||
export const fs = require("node:fs/promises").$data;
|
||||
|
||||
export const fsStreamInternals = {
|
||||
writeStreamFastPath(str) {
|
||||
return str[require("internal/fs/streams").kWriteStreamFastPath];
|
||||
|
||||
422
src/js/internal/fs/FileHandle.ts
Normal file
422
src/js/internal/fs/FileHandle.ts
Normal file
@@ -0,0 +1,422 @@
|
||||
// Moved from src/js/node/fs.promises.ts
|
||||
const types = require("node:util/types");
|
||||
const EventEmitter = require("node:events");
|
||||
const { validateInteger } = require("internal/validators");
|
||||
|
||||
const PromisePrototypeFinally = Promise.prototype.finally;
|
||||
const SymbolAsyncDispose = Symbol.asyncDispose;
|
||||
const ObjectFreeze = Object.freeze;
|
||||
|
||||
const { kFd } = require("internal/shared");
|
||||
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 kTransfer = Symbol("kTransfer");
|
||||
const kTransferList = Symbol("kTransferList");
|
||||
const kDeserialize = Symbol("kDeserialize");
|
||||
const kEmptyObject = ObjectFreeze({ __proto__: null });
|
||||
const kFlag = Symbol("kFlag");
|
||||
|
||||
let writeFile: typeof import("fs/promises").writeFile,
|
||||
readFile: typeof import("fs/promises").readFile,
|
||||
fchmod: typeof import("fs").fchmod.__promisify__,
|
||||
fchown: typeof import("fs").fchown.__promisify__,
|
||||
fdatasync: typeof import("fs").fdatasync.__promisify__,
|
||||
fsync: typeof import("fs").fsync.__promisify__,
|
||||
read: typeof import("fs").read.__promisify__,
|
||||
readv: typeof import("fs").readv.__promisify__,
|
||||
fstat: typeof import("fs").fstat.__promisify__,
|
||||
ftruncate: typeof import("fs").ftruncate.__promisify__,
|
||||
futimes: typeof import("fs").futimes.__promisify__,
|
||||
write: typeof import("fs").write.__promisify__,
|
||||
writev: typeof import("fs").writev.__promisify__,
|
||||
close: typeof import("fs").close.__promisify__;
|
||||
|
||||
// Avoid circular dependency
|
||||
function setFSExports(exports: any) {
|
||||
({
|
||||
writeFile,
|
||||
readFile,
|
||||
fchmod,
|
||||
fchown,
|
||||
fdatasync,
|
||||
fsync,
|
||||
read,
|
||||
readv,
|
||||
fstat,
|
||||
ftruncate,
|
||||
futimes,
|
||||
write,
|
||||
writev,
|
||||
close,
|
||||
} = exports);
|
||||
}
|
||||
|
||||
class FileHandle extends EventEmitter {
|
||||
constructor(fd, flag) {
|
||||
super();
|
||||
this[kFd] = fd ? fd : -1;
|
||||
this[kRefs] = 1;
|
||||
this[kClosePromise] = null;
|
||||
this[kFlag] = flag;
|
||||
}
|
||||
|
||||
getAsyncId() {
|
||||
throw new Error("BUN TODO FileHandle.getAsyncId");
|
||||
}
|
||||
|
||||
get fd() {
|
||||
return this[kFd];
|
||||
}
|
||||
|
||||
[kCloseResolve];
|
||||
[kFlag];
|
||||
[kClosePromise];
|
||||
[kRefs];
|
||||
[Symbol("messaging_transfer_symbol")]() {}
|
||||
|
||||
async appendFile(data, options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("writeFile", fd);
|
||||
let encoding: BufferEncoding | null = "utf8";
|
||||
let flush = false;
|
||||
if (options == null || typeof options === "function") {
|
||||
} else if (typeof options === "string") {
|
||||
encoding = options as BufferEncoding;
|
||||
} else {
|
||||
encoding = options?.encoding ?? encoding;
|
||||
flush = options?.flush ?? flush;
|
||||
}
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writeFile(fd, data, { encoding, flush, flag: this[kFlag] });
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async chmod(mode) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fchmod", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fchmod(fd, mode);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async chown(uid, gid) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fchown", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fchown(fd, uid, gid);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async datasync() {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fdatasync", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fdatasync(fd);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async sync() {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fsync", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fsync(fd);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async read(bufferOrParams, offset, length, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fsync", fd);
|
||||
|
||||
let buffer = bufferOrParams;
|
||||
if (!types.isArrayBufferView(buffer)) {
|
||||
if (bufferOrParams !== undefined) {
|
||||
if (typeof bufferOrParams !== "object" || $isArray(bufferOrParams)) {
|
||||
throw $ERR_INVALID_ARG_TYPE("options", "object", bufferOrParams);
|
||||
}
|
||||
}
|
||||
({
|
||||
buffer = Buffer.alloc(16384),
|
||||
offset = 0,
|
||||
length = buffer.byteLength - offset,
|
||||
position = null,
|
||||
} = bufferOrParams ?? kEmptyObject);
|
||||
}
|
||||
|
||||
if (offset !== null && typeof offset === "object") {
|
||||
({ offset = 0, length = buffer?.byteLength - offset, position = null } = offset);
|
||||
}
|
||||
|
||||
if (offset == null) {
|
||||
offset = 0;
|
||||
} else {
|
||||
validateInteger(offset, "offset", 0);
|
||||
}
|
||||
|
||||
length ??= buffer?.byteLength - offset;
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
const bytesRead = await read(fd, buffer, offset, length, position);
|
||||
return { buffer, bytesRead };
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async readv(buffers, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("readv", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await readv(fd, buffers, position);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async readFile(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("readFile", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await readFile(fd, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
readLines(_options = undefined) {
|
||||
throw new Error("BUN TODO FileHandle.readLines");
|
||||
}
|
||||
|
||||
async stat(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fstat", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fstat(fd, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async truncate(len = 0) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("ftruncate", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await ftruncate(fd, len);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async utimes(atime, mtime) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("futimes", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await futimes(fd, atime, mtime);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async write(buffer, offset, length, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("write", fd);
|
||||
|
||||
if (buffer?.byteLength === 0) return { __proto__: null, bytesWritten: 0, buffer };
|
||||
|
||||
let isArrayBufferView;
|
||||
isArrayBufferView ??= require("node:util/types").isArrayBufferView;
|
||||
if (isArrayBufferView(buffer)) {
|
||||
if (typeof offset === "object") {
|
||||
({ offset = 0, length = buffer.byteLength - offset, position = null } = offset ?? kEmptyObject);
|
||||
}
|
||||
|
||||
if (offset == null) {
|
||||
offset = 0;
|
||||
}
|
||||
if (typeof length !== "number") length = buffer.byteLength - offset;
|
||||
if (typeof position !== "number") position = null;
|
||||
}
|
||||
try {
|
||||
this[kRef]();
|
||||
return {
|
||||
buffer,
|
||||
bytesWritten: await write(fd, buffer, offset, length, position),
|
||||
};
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async writev(buffers, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("writev", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writev(fd, buffers, position);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async writeFile(data: string, options: any = "utf8") {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("writeFile", fd);
|
||||
let encoding: BufferEncoding | null = "utf8";
|
||||
let signal: AbortSignal | undefined = undefined;
|
||||
|
||||
if (options == null || typeof options === "function") {
|
||||
} else if (typeof options === "string") {
|
||||
encoding = options as BufferEncoding;
|
||||
} else {
|
||||
encoding = options?.encoding ?? encoding;
|
||||
signal = options?.signal ?? undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writeFile(fd, data, { encoding, flag: this[kFlag], signal });
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
close = () => {
|
||||
const fd = this[kFd];
|
||||
if (fd === -1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (this[kClosePromise]) {
|
||||
return this[kClosePromise];
|
||||
}
|
||||
|
||||
if (--this[kRefs] === 0) {
|
||||
this[kFd] = -1;
|
||||
this[kClosePromise] = PromisePrototypeFinally.$call(close(fd), () => {
|
||||
this[kClosePromise] = undefined;
|
||||
});
|
||||
} else {
|
||||
this[kClosePromise] = PromisePrototypeFinally.$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 [SymbolAsyncDispose]() {
|
||||
return this.close();
|
||||
}
|
||||
|
||||
readableWebStream(_options = kEmptyObject) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("readableWebStream", fd);
|
||||
|
||||
return Bun.file(fd).stream();
|
||||
}
|
||||
|
||||
createReadStream(options = kEmptyObject) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("createReadStream", fd);
|
||||
return new (require("internal/fs/streams").ReadStream)(undefined, {
|
||||
highWaterMark: 64 * 1024,
|
||||
...options,
|
||||
fd: this,
|
||||
});
|
||||
}
|
||||
|
||||
createWriteStream(options = kEmptyObject) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("createWriteStream", fd);
|
||||
return new (require("internal/fs/streams").WriteStream)(undefined as any, {
|
||||
highWaterMark: 64 * 1024,
|
||||
...options,
|
||||
fd: this,
|
||||
});
|
||||
}
|
||||
|
||||
[kTransfer]() {
|
||||
throw new Error("BUN TODO FileHandle.kTransfer");
|
||||
}
|
||||
|
||||
[kTransferList]() {
|
||||
throw new Error("BUN TODO FileHandle.kTransferList");
|
||||
}
|
||||
|
||||
[kDeserialize](_) {
|
||||
throw new Error("BUN TODO FileHandle.kDeserialize");
|
||||
}
|
||||
|
||||
[kRef]() {
|
||||
this[kRefs]++;
|
||||
}
|
||||
|
||||
[kUnref]() {
|
||||
if (--this[kRefs] === 0) {
|
||||
this[kFd] = -1;
|
||||
this.close().$then(this[kCloseResolve], this[kCloseReject]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function throwEBADFIfNecessary(fn: string, fd) {
|
||||
if (fd === -1) {
|
||||
const err: any = new Error("Bad file descriptor");
|
||||
err.code = "EBADF";
|
||||
err.name = "SystemError";
|
||||
err.syscall = fn;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
FileHandle,
|
||||
kFd,
|
||||
kRef,
|
||||
kUnref,
|
||||
setFSExports,
|
||||
};
|
||||
3
src/js/internal/fs/binding.ts
Normal file
3
src/js/internal/fs/binding.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const fs = $zig("node_fs_binding.zig", "createBinding") as $ZigGeneratedClasses.NodeJSFS;
|
||||
|
||||
export default fs;
|
||||
@@ -3,13 +3,7 @@ import type { FileSink } from "bun";
|
||||
const { Readable, Writable, finished } = require("node:stream");
|
||||
const fs: typeof import("node:fs") = require("node:fs");
|
||||
const { read, write, fsync, writev } = fs;
|
||||
const { FileHandle, kRef, kUnref, kFd } = (fs.promises as any).$data as {
|
||||
FileHandle: { new (): FileHandle };
|
||||
readonly kRef: unique symbol;
|
||||
readonly kUnref: unique symbol;
|
||||
readonly kFd: unique symbol;
|
||||
fs: typeof fs;
|
||||
};
|
||||
const { FileHandle, kRef, kUnref, kFd } = require("internal/fs/FileHandle");
|
||||
type FileHandle = import("node:fs/promises").FileHandle & {
|
||||
on(event: any, listener: any): FileHandle;
|
||||
};
|
||||
|
||||
@@ -110,4 +110,5 @@ export default {
|
||||
kWeakHandler: Symbol("kWeak"),
|
||||
kGetNativeReadableProto: Symbol("kGetNativeReadableProto"),
|
||||
kEmptyObject,
|
||||
};
|
||||
kFd: Symbol("kFd"),
|
||||
} as const;
|
||||
|
||||
@@ -1,28 +1,8 @@
|
||||
// Hardcoded module "node:fs/promises"
|
||||
const types = require("node:util/types");
|
||||
const EventEmitter = require("node:events");
|
||||
const fs = $zig("node_fs_binding.zig", "createBinding") as $ZigGeneratedClasses.NodeJSFS;
|
||||
const fs = require("internal/fs/binding");
|
||||
const { glob } = require("internal/fs/glob");
|
||||
const constants = $processBindingConstants.fs;
|
||||
|
||||
var PromisePrototypeFinally = Promise.prototype.finally; //TODO
|
||||
var SymbolAsyncDispose = Symbol.asyncDispose;
|
||||
var ObjectFreeze = Object.freeze;
|
||||
|
||||
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 kTransfer = Symbol("kTransfer");
|
||||
const kTransferList = Symbol("kTransferList");
|
||||
const kDeserialize = Symbol("kDeserialize");
|
||||
const kEmptyObject = ObjectFreeze({ __proto__: null });
|
||||
const kFlag = Symbol("kFlag");
|
||||
|
||||
const { validateInteger } = require("internal/validators");
|
||||
const { kFd } = require("internal/shared");
|
||||
|
||||
function watch(
|
||||
filename: string | Buffer | URL,
|
||||
@@ -115,13 +95,7 @@ async function opendir(dir: string, options) {
|
||||
return new (require("node:fs").Dir)(1, dir, options);
|
||||
}
|
||||
|
||||
const private_symbols = {
|
||||
kRef,
|
||||
kUnref,
|
||||
kFd,
|
||||
FileHandle: null as any,
|
||||
fs,
|
||||
};
|
||||
let FileHandle: typeof import("internal/fs/FileHandle").default.FileHandle;
|
||||
|
||||
const _readFile = fs.readFile.bind(fs);
|
||||
const _writeFile = fs.writeFile.bind(fs);
|
||||
@@ -161,7 +135,11 @@ const exports = {
|
||||
mkdtemp: asyncWrap(fs.mkdtemp, "mkdtemp"),
|
||||
statfs: asyncWrap(fs.statfs, "statfs"),
|
||||
open: async (path, flags = "r", mode = 0o666) => {
|
||||
return new private_symbols.FileHandle(await fs.open(path, flags, mode), flags);
|
||||
if (!FileHandle) {
|
||||
lazyInitFileHandle();
|
||||
}
|
||||
|
||||
return new FileHandle(await fs.open(path, flags, mode), flags);
|
||||
},
|
||||
read: asyncWrap(fs.read, "read"),
|
||||
write: asyncWrap(fs.write, "write"),
|
||||
@@ -213,11 +191,8 @@ const exports = {
|
||||
constants,
|
||||
watch,
|
||||
opendir,
|
||||
|
||||
// "$data" is reuse of private symbol
|
||||
// this is used to export the private symbols to 'fs.js' without making it public.
|
||||
$data: private_symbols,
|
||||
};
|
||||
|
||||
export default exports;
|
||||
|
||||
// TODO: remove this in favor of just returning js functions that don't check `this`
|
||||
@@ -225,392 +200,17 @@ function asyncWrap(fn: any, name: string) {
|
||||
const wrapped = async function (...args) {
|
||||
return fn.$apply(fs, args);
|
||||
};
|
||||
Object.defineProperty(wrapped, "name", { value: name });
|
||||
Object.defineProperty(wrapped, "length", { value: fn.length });
|
||||
const descriptor = { value: name, enumerable: false, configurable: true };
|
||||
Object.$defineProperty(wrapped, "name", descriptor);
|
||||
Object.$defineProperty(wrapped, "displayName", descriptor);
|
||||
Object.$defineProperty(wrapped, "length", { value: fn.length });
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
{
|
||||
const {
|
||||
writeFile,
|
||||
readFile,
|
||||
fchmod,
|
||||
fchown,
|
||||
fdatasync,
|
||||
fsync,
|
||||
read,
|
||||
readv,
|
||||
fstat,
|
||||
ftruncate,
|
||||
futimes,
|
||||
write,
|
||||
writev,
|
||||
close,
|
||||
} = exports;
|
||||
let isArrayBufferView;
|
||||
|
||||
// Partially taken from https://github.com/nodejs/node/blob/c25878d370/lib/internal/fs/promises.js#L148
|
||||
// These functions await the result so that errors propagate correctly with
|
||||
// async stack traces and so that the ref counting is correct.
|
||||
class FileHandle extends EventEmitter {
|
||||
constructor(fd, flag) {
|
||||
super();
|
||||
this[kFd] = fd ? fd : -1;
|
||||
this[kRefs] = 1;
|
||||
this[kClosePromise] = null;
|
||||
this[kFlag] = flag;
|
||||
}
|
||||
|
||||
getAsyncId() {
|
||||
throw new Error("BUN TODO FileHandle.getAsyncId");
|
||||
}
|
||||
|
||||
get fd() {
|
||||
return this[kFd];
|
||||
}
|
||||
|
||||
[kCloseResolve];
|
||||
[kFd];
|
||||
[kFlag];
|
||||
[kClosePromise];
|
||||
[kRefs];
|
||||
// needs to exist for https://github.com/nodejs/node/blob/8641d941893/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js to pass
|
||||
[Symbol("messaging_transfer_symbol")]() {}
|
||||
|
||||
async appendFile(data, options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("writeFile", fd);
|
||||
let encoding = "utf8";
|
||||
let flush = false;
|
||||
if (options == null || typeof options === "function") {
|
||||
} else if (typeof options === "string") {
|
||||
encoding = options;
|
||||
} else {
|
||||
encoding = options?.encoding ?? encoding;
|
||||
flush = options?.flush ?? flush;
|
||||
}
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writeFile(fd, data, { encoding, flush, flag: this[kFlag] });
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async chmod(mode) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fchmod", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fchmod(fd, mode);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async chown(uid, gid) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fchown", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fchown(fd, uid, gid);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async datasync() {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fdatasync", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fdatasync(fd);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async sync() {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fsync", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fsync(fd);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async read(bufferOrParams, offset, length, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fsync", fd);
|
||||
|
||||
let buffer = bufferOrParams;
|
||||
if (!types.isArrayBufferView(buffer)) {
|
||||
// This is fh.read(params)
|
||||
if (bufferOrParams !== undefined) {
|
||||
// validateObject(bufferOrParams, 'options', kValidateObjectAllowNullable);
|
||||
if (typeof bufferOrParams !== "object" || $isArray(bufferOrParams)) {
|
||||
throw $ERR_INVALID_ARG_TYPE("options", "object", bufferOrParams);
|
||||
}
|
||||
}
|
||||
({
|
||||
buffer = Buffer.alloc(16384),
|
||||
offset = 0,
|
||||
length = buffer.byteLength - offset,
|
||||
position = null,
|
||||
} = bufferOrParams ?? kEmptyObject);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
const bytesRead = await read(fd, buffer, offset, length, position);
|
||||
return { buffer, bytesRead };
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async readv(buffers, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("readv", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await readv(fd, buffers, position);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async readFile(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("readFile", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await readFile(fd, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
readLines(_options = undefined) {
|
||||
throw new Error("BUN TODO FileHandle.readLines");
|
||||
}
|
||||
|
||||
async stat(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("fstat", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fstat(fd, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async truncate(len = 0) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("ftruncate", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await ftruncate(fd, len);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async utimes(atime, mtime) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("futimes", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await futimes(fd, atime, mtime);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async write(buffer, offset, length, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("write", fd);
|
||||
|
||||
if (buffer?.byteLength === 0) return { __proto__: null, bytesWritten: 0, buffer };
|
||||
|
||||
isArrayBufferView ??= require("node:util/types").isArrayBufferView;
|
||||
if (isArrayBufferView(buffer)) {
|
||||
if (typeof offset === "object") {
|
||||
({ offset = 0, length = buffer.byteLength - offset, position = null } = offset ?? kEmptyObject);
|
||||
}
|
||||
|
||||
if (offset == null) {
|
||||
offset = 0;
|
||||
}
|
||||
if (typeof length !== "number") length = buffer.byteLength - offset;
|
||||
if (typeof position !== "number") position = null;
|
||||
}
|
||||
try {
|
||||
this[kRef]();
|
||||
return { buffer, bytesWritten: await write(fd, buffer, offset, length, position) };
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async writev(buffers, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("writev", fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writev(fd, buffers, position);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async writeFile(data: string, options: any = "utf8") {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("writeFile", fd);
|
||||
let encoding: string = "utf8";
|
||||
let signal: AbortSignal | undefined = undefined;
|
||||
|
||||
if (options == null || typeof options === "function") {
|
||||
} else if (typeof options === "string") {
|
||||
encoding = options;
|
||||
} else {
|
||||
encoding = options?.encoding ?? encoding;
|
||||
signal = options?.signal ?? undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writeFile(fd, data, { encoding, flag: this[kFlag], signal });
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
close = () => {
|
||||
const fd = this[kFd];
|
||||
if (fd === -1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (this[kClosePromise]) {
|
||||
return this[kClosePromise];
|
||||
}
|
||||
|
||||
if (--this[kRefs] === 0) {
|
||||
this[kFd] = -1;
|
||||
this[kClosePromise] = PromisePrototypeFinally.$call(close(fd), () => {
|
||||
this[kClosePromise] = undefined;
|
||||
});
|
||||
} else {
|
||||
this[kClosePromise] = PromisePrototypeFinally.$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 [SymbolAsyncDispose]() {
|
||||
return this.close();
|
||||
}
|
||||
|
||||
readableWebStream(_options = kEmptyObject) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("readableWebStream", fd);
|
||||
|
||||
return Bun.file(fd).stream();
|
||||
}
|
||||
|
||||
createReadStream(options = kEmptyObject) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("createReadStream", fd);
|
||||
return new (require("internal/fs/streams").ReadStream)(undefined, {
|
||||
highWaterMark: 64 * 1024,
|
||||
...options,
|
||||
fd: this,
|
||||
});
|
||||
}
|
||||
|
||||
createWriteStream(options = kEmptyObject) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary("createWriteStream", fd);
|
||||
return new (require("internal/fs/streams").WriteStream)(undefined, {
|
||||
highWaterMark: 64 * 1024,
|
||||
...options,
|
||||
fd: this,
|
||||
});
|
||||
}
|
||||
|
||||
[kTransfer]() {
|
||||
throw new Error("BUN TODO FileHandle.kTransfer");
|
||||
}
|
||||
|
||||
[kTransferList]() {
|
||||
throw new Error("BUN TODO FileHandle.kTransferList");
|
||||
}
|
||||
|
||||
[kDeserialize](_) {
|
||||
throw new Error("BUN TODO FileHandle.kDeserialize");
|
||||
}
|
||||
|
||||
[kRef]() {
|
||||
this[kRefs]++;
|
||||
}
|
||||
|
||||
[kUnref]() {
|
||||
if (--this[kRefs] === 0) {
|
||||
this[kFd] = -1;
|
||||
this.close().$then(this[kCloseResolve], this[kCloseReject]);
|
||||
}
|
||||
}
|
||||
}
|
||||
private_symbols.FileHandle = FileHandle;
|
||||
}
|
||||
|
||||
function throwEBADFIfNecessary(fn: string, fd) {
|
||||
if (fd === -1) {
|
||||
const err: any = new Error("Bad file descriptor");
|
||||
err.code = "EBADF";
|
||||
err.name = "SystemError";
|
||||
err.syscall = fn;
|
||||
throw err;
|
||||
}
|
||||
function lazyInitFileHandle() {
|
||||
const lazyInitFileHandle = require("internal/fs/FileHandle");
|
||||
FileHandle = lazyInitFileHandle.FileHandle;
|
||||
lazyInitFileHandle.setFSExports(exports);
|
||||
}
|
||||
|
||||
async function writeFileAsyncIteratorInner(fd, iterable, encoding, signal: AbortSignal | null) {
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
// Hardcoded module "node:fs"
|
||||
import type { Dirent as DirentType, PathLike, Stats as StatsType } from "fs";
|
||||
const EventEmitter = require("node:events");
|
||||
const promises = require("node:fs/promises");
|
||||
|
||||
const types = require("node:util/types");
|
||||
const { validateFunction, validateInteger } = require("internal/validators");
|
||||
|
||||
const kEmptyObject = Object.freeze(Object.create(null));
|
||||
const { kEmptyObject } = require("internal/shared");
|
||||
const fs = require("internal/fs/binding");
|
||||
|
||||
const isDate = types.isDate;
|
||||
|
||||
// Private exports
|
||||
// `fs` points to the return value of `node_fs_binding.zig`'s `createBinding` function.
|
||||
const { fs } = promises.$data;
|
||||
|
||||
const constants = $processBindingConstants.fs;
|
||||
var _lazyGlob;
|
||||
function lazyGlob() {
|
||||
return (_lazyGlob ??= require("internal/fs/glob"));
|
||||
}
|
||||
|
||||
function ensureCallback(callback) {
|
||||
function ensureCallback(callback: unknown): asserts callback is (...args: any[]) => void {
|
||||
if (!$isCallable(callback)) {
|
||||
throw $ERR_INVALID_ARG_TYPE("cb", "function", callback);
|
||||
}
|
||||
@@ -789,7 +785,7 @@ const realpathSync: typeof import("node:fs").realpathSync =
|
||||
}
|
||||
|
||||
let resolvedLink;
|
||||
lastStat = fs.lstatSync(base, { throwIfNoEntry: true });
|
||||
lastStat = fs.lstatSync(base, { throwIfNoEntry: true }) as StatsType;
|
||||
if (lastStat === undefined) return;
|
||||
|
||||
if (!lastStat.isSymbolicLink()) {
|
||||
@@ -797,7 +793,7 @@ const realpathSync: typeof import("node:fs").realpathSync =
|
||||
continue;
|
||||
}
|
||||
|
||||
lastStat = fs.statSync(base, { throwIfNoEntry: true });
|
||||
lastStat = fs.statSync(base, { throwIfNoEntry: true }) as StatsType;
|
||||
const linkTarget = fs.readlinkSync(base);
|
||||
resolvedLink = pathModule.resolve(previous, linkTarget);
|
||||
|
||||
@@ -810,7 +806,7 @@ const realpathSync: typeof import("node:fs").realpathSync =
|
||||
|
||||
// On windows, check that the root exists. On unix there is no need.
|
||||
if (!knownHard.$has(base)) {
|
||||
lastStat = fs.lstatSync(base, { throwIfNoEntry: true });
|
||||
lastStat = fs.lstatSync(base, { throwIfNoEntry: true }) as StatsType;
|
||||
if (lastStat === undefined) return;
|
||||
knownHard.$add(base);
|
||||
}
|
||||
@@ -1001,7 +997,7 @@ function cp(src, dest, options, callback) {
|
||||
|
||||
ensureCallback(callback);
|
||||
|
||||
promises.cp(src, dest, options).then(() => callback(), callback);
|
||||
exports.promises.cp(src, dest, options).then(() => callback(), callback);
|
||||
}
|
||||
|
||||
function _toUnixTimestamp(time: any, name = "time") {
|
||||
@@ -1054,7 +1050,7 @@ class Dir {
|
||||
withFileTypes: true,
|
||||
encoding: this.#options?.encoding,
|
||||
recursive: this.#options?.recursive,
|
||||
}));
|
||||
}) as DirentType[]);
|
||||
return entries.shift() ?? null;
|
||||
}
|
||||
|
||||
@@ -1075,8 +1071,8 @@ class Dir {
|
||||
recursive: this.#options?.recursive,
|
||||
})
|
||||
.then(entries => {
|
||||
this.#entries = entries;
|
||||
return entries.shift() ?? null;
|
||||
this.#entries = entries as DirentType[];
|
||||
return (entries as DirentType[]).shift() ?? null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1271,13 +1267,32 @@ var exports = {
|
||||
configurable: true,
|
||||
});
|
||||
},
|
||||
promises,
|
||||
get promises() {
|
||||
const promises = require("node:fs/promises");
|
||||
Object.defineProperty(exports, "promises", {
|
||||
value: promises,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
return promises;
|
||||
},
|
||||
set promises(value) {
|
||||
Object.defineProperty(exports, "promises", {
|
||||
value,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
export default exports;
|
||||
|
||||
// Preserve the names
|
||||
function setName(fn, value) {
|
||||
Object.$defineProperty(fn, "name", { value, enumerable: false, configurable: true });
|
||||
const descriptor = { value, enumerable: false, configurable: true };
|
||||
Object.$defineProperty(fn, "name", descriptor);
|
||||
Object.$defineProperty(fn, "displayName", descriptor);
|
||||
}
|
||||
setName(Dirent, "Dirent");
|
||||
setName(FSWatcher, "FSWatcher");
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
* originally developed by Node.js contributors and Joyent, Inc.
|
||||
*
|
||||
* Copyright Node.js contributors. All rights reserved.
|
||||
* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
@@ -27,13 +25,11 @@
|
||||
* Modifications were made to the original code.
|
||||
*/
|
||||
const { isTypedArray } = require("node:util/types");
|
||||
const { hideFromStack, throwNotImplemented } = require("internal/shared");
|
||||
const { hideFromStack, kFd } = require("internal/shared");
|
||||
const { STATUS_CODES } = require("internal/http");
|
||||
const tls = require("node:tls");
|
||||
const net = require("node:net");
|
||||
const fs = require("node:fs");
|
||||
const { $data } = require("node:fs/promises");
|
||||
const FileHandle = $data.FileHandle;
|
||||
const bunTLSConnectOptions = Symbol.for("::buntlsconnectoptions::");
|
||||
const bunSocketServerOptions = Symbol.for("::bunnetserveroptions::");
|
||||
const kInfoHeaders = Symbol("sent-info-headers");
|
||||
@@ -2170,8 +2166,8 @@ class ServerHttp2Stream extends Http2Stream {
|
||||
if (options.statCheck !== undefined && typeof options.statCheck !== "function") {
|
||||
throw $ERR_INVALID_ARG_VALUE("options.statCheck", options.statCheck);
|
||||
}
|
||||
if (fd instanceof FileHandle) {
|
||||
fs.fstat(fd.fd, doSendFileFD.bind(this, options, fd, headers));
|
||||
if (fd?.[kFd]) {
|
||||
fs.fstat(fd?.[kFd], doSendFileFD.bind(this, options, fd, headers));
|
||||
} else {
|
||||
fs.fstat(fd, doSendFileFD.bind(this, options, fd, headers));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user