import { promiseResolve, promiseThen as then, safePromise, } from "ext:core/promises"; import { ArrayIsArray, DatePrototypeGetTime as getTime, getter, NumberIsFinite as numberIsFinite, ObjectCreate, ObjectDefineProperty as define, ObjectFreeze as freeze, RangeError, ReflectApply as apply, SymbolIterator, TypedArrayPrototypeGetBuffer as bufferOf, TypedArrayPrototypeGetByteLength as length, TypedArrayPrototypeGetByteOffset as offsetOf, TypedArrayPrototypeGetTag as tag, TypeError, Uint8Array, Uint8ArrayPrototypeSet as setBytes, uncurryThis, WeakMap, WeakMapPrototypeGet as get, WeakMapPrototypeSet as set, } from "ext:core/primordials"; import type { Metadata, WatchEvent } from "ext:core/ops"; import { op_file_chmod as chmodOp, op_file_chown as chownOp, op_file_close as closeOp, op_file_copy as copyOp, op_file_fchmod as fchmodOp, op_file_fchmod_sync as fchmodSyncOp, op_file_fchown as fchownOp, op_file_fchown_sync as fchownSyncOp, op_file_flock as flockOp, op_file_flock_sync as flockSyncOp, op_file_funlock as funlockOp, op_file_funlock_sync as funlockSyncOp, op_file_futime as futimeOp, op_file_futime_sync as futimeSyncOp, op_file_lchown as lchownOp, op_file_link as linkOp, op_file_lutime as lutimeOp, op_file_mkdir as mkdirOp, op_file_open as openOp, op_file_path_stat as pathStatOp, op_file_path_truncate as truncatePathOp, op_file_read as readOp, op_file_read_dir as readDirOp, op_file_read_link as readLinkOp, op_file_read_sync as readSyncOp, op_file_real_path as realPathOp, op_file_remove as removeOp, op_file_rename as renameOp, op_file_seek as seekOp, op_file_seek_sync as seekSyncOp, op_file_stat as statOp, op_file_stat_sync as statSyncOp, op_file_symlink as symlinkOp, op_file_sync as syncOp, op_file_sync_sync as syncSyncOp, op_file_truncate as truncateOp, op_file_truncate_sync as truncateSyncOp, op_file_url_path as urlPath, op_file_utime as utimeOp, op_file_watch as watchOp, op_file_watch_close as watchCloseOp, op_file_watch_keepalive as watchKeepAliveOp, op_file_watch_next as watchNextOp, op_file_watch_next_unref as watchNextUnrefOp, op_file_watch_ref as watchRefOp, op_file_write as writeOp, op_file_write_sync as writeSyncOp, op_file_writev as writevOp, op_file_writev_sync as writevSyncOp, } from "ext:core/ops"; import { array, dictionary, isObject, required, state } from "ext:web/idl.js"; import { addListener, removeListener, signals } from "ext:web/events.js"; import { registerUncloneable } from "ext:web/serialization.js"; import { URL } from "ext:url/main.js"; import type { ReadableByteStreamController, WritableStreamDefaultController as WritableController, } from "ext:webstreams/streams.js"; import { byobRequest, enqueue, errorStream, finish, ReadableStream, ReadableStreamBYOBRequest, WritableStream, } from "ext:webstreams/streams.js"; import { errors, failure, result } from "./errors.js"; const handles = new WeakMap(); registerUncloneable(handles); const watchers = new WeakMap(); registerUncloneable(watchers); const Date = globalThis.Date, FinalizationRegistry = globalThis.FinalizationRegistry; const register = uncurryThis(FinalizationRegistry.prototype.register), unregister = uncurryThis(FinalizationRegistry.prototype.unregister); const registry = new FinalizationRegistry((id: number) => closeOp(args(id, true)) ); const watchRegistry = new FinalizationRegistry((id: number) => watchCloseOp(args(id, true)) ); const urlHref = getter(URL.prototype, "href"), getView = getter(ReadableStreamBYOBRequest.prototype, "view"); const writableError = uncurryThis( WritableStreamDefaultController.prototype.error, ); const dispose: typeof Symbol.dispose = Symbol.dispose, asyncIterator: typeof Symbol.asyncIterator = Symbol.asyncIterator, empty = new Uint8Array(); const watchKinds = freeze( ["create", "modify", "remove", "rename", "rescan"] as const, ); const safeInteger = Number.isSafeInteger, mathTrunc = Math.trunc, mathRound = Math.round; const SeekMode = freeze({ Start: 0, Current: 1, End: 2 }); function args(...items: T): T { const result = array(); for (let i = 0; i < items.length; i++) result[i] = items[i]; return result as unknown as T; } function path(value: unknown): string { if (typeof value === "string") return value; let href; try { href = urlHref(value); } catch { throw new TypeError("Expected a path string or file URL"); } return result(urlPath(href)); } interface HandleState { id: number | null; owner: object; readable: ReadableStream | null; writable: WritableStream | null; reader: ReadableByteStreamController | null; writer: WritableController | null; } interface WatchState { id: number | null; owner: object; referenced: boolean; epoch: number; signal: unknown; onAbort: (() => void) | null; } function id(data: { id: number | null }): number { if (data.id === null) throw failure("BadResource", "File handle is closed"); return data.id; } function bytes(value: unknown): Uint8Array { if (tag(value) !== "Uint8Array") { throw new TypeError("File I/O requires a Uint8Array"); } return value as Uint8Array; } function segment( buffer: Uint8Array, offset: number, ): Uint8Array { const remaining = length(buffer) - offset; return new Uint8Array( bufferOf(buffer), offsetOf(buffer) + offset, remaining < 65536 ? remaining : 65536, ); } function vectors(buffers: unknown): Uint8Array[] { if (!ArrayIsArray(buffers)) { throw new TypeError("File I/O requires an array of Uint8Array"); } // Snapshot before queueing, without rereading caller-controlled elements. const count = buffers.length, pieces = args[]>(); if (!safeInteger(count) || count < 0 || count > 4294967295) { throw new RangeError("Invalid vector length"); } let total = 0; for (let i = 0; i < count; i++) { const view = buffers[i] as Uint8Array; if (tag(view) !== "Uint8Array") { throw new TypeError("File I/O requires an array of Uint8Array"); } setBytes(view, empty); const size = length(view); if (size === 0) continue; if (!safeInteger(total + size)) { throw new RangeError("Vector exceeds safe integer range"); } total += size; // Fixed snapshot for the native call, which needs unshared storage. pieces[pieces.length] = new Uint8Array( new Uint8Array(bufferOf(view), offsetOf(view), size), ); } return pieces; } function integer(value: unknown, negative = false): number { if (!safeInteger(value) || (!negative && (value as number) < 0)) { throw new RangeError("Expected a safe integer"); } return value as number; } function mode(value: number): number { if (integer(value) > 0o7777) { throw new RangeError("Invalid file mode"); } return value; } function owner(value: number | null | undefined): number | null { if (value === null || value === undefined) return null; if (integer(value) >= 4294967295) { throw new RangeError("Invalid user or group id"); } return value; } function stamp(value: number | Date): [number, number] { let seconds, nanos; if (value instanceof Date) { const ms = getTime(value); if (!numberIsFinite(ms)) { throw new TypeError("Expected a timestamp in seconds or a Date"); } // Split integer milliseconds exactly: float seconds would smear the // sub-millisecond remainder across the nanosecond field. seconds = mathTrunc(ms / 1000); nanos = (ms - seconds * 1000) * 1000000; } else { if (typeof value !== "number" || !numberIsFinite(value)) { throw new TypeError("Expected a timestamp in seconds or a Date"); } seconds = mathTrunc(value); nanos = mathRound((value - seconds) * 1e9); } if (nanos < 0) { seconds -= 1; nanos += 1e9; } else if (nanos >= 1e9) { seconds += 1; nanos -= 1e9; } if ( seconds < -8640000000000 || seconds > 8640000000000 || (seconds === 8640000000000 && nanos > 0) ) { throw new RangeError("Timestamp out of range"); } return [seconds, nanos]; } function metadata(row: Metadata) { return { isFile: row[0], isDirectory: row[1], isSymlink: row[2], size: row[3], mtime: row[4] === null ? null : new Date(row[4]), atime: row[5] === null ? null : new Date(row[5]), birthtime: row[6] === null ? null : new Date(row[6]), mode: row[7], uid: row[8], gid: row[9], }; } function watchId(data: WatchState): number { if (data.id === null) { throw failure("BadResource", "Filesystem watcher is closed"); } return data.id; } function closeWatcher(data: WatchState, quiet = false): void { if (data.id === null) { if (!quiet) watchId(data); return; } const current = data.id; data.id = null; unregister(watchRegistry, data.owner); if (data.signal !== undefined && data.onAbort !== null) { removeListener(data.signal, "abort", data.onAbort); } data.signal = undefined; data.onAbort = null; result(watchCloseOp(args(current, quiet))); } function keepAlive(data: WatchState, epoch: number): void { if (data.id === null) return; then(watchKeepAliveOp(args(data.id, epoch)), () => {}, () => {}); } function watchEvent(row: WatchEvent) { return freeze({ __proto__: null, kind: watchKinds[row[0]], paths: freeze(row[1]), }); } class FsWatcher { declare static readonly [Symbol.hasInstance]: (value: unknown) => value is FsWatcher; constructor() { throw new TypeError("Illegal constructor"); } close() { closeWatcher(state(watchers, this)); } ref() { const data = state(watchers, this); if (data.id === null || data.referenced) return; data.referenced = true; data.epoch = result(watchRefOp(data.id)); keepAlive(data, data.epoch); } unref() { const data = state(watchers, this); if (data.id === null || !data.referenced) return; data.referenced = false; data.epoch = result(watchRefOp(data.id)); } [dispose]() { closeWatcher(state(watchers, this), true); } [asyncIterator](): AsyncIterableIterator, undefined> { const data = state(watchers, this); return { async next() { if (data.id === null) return { done: true, value: undefined }; try { const row = result( await safePromise( (data.referenced ? watchNextOp : watchNextUnrefOp)( watchId(data), ), ), ); if (row === null || data.id === null) { closeWatcher(data, true); return { done: true, value: undefined }; } return { done: false, value: watchEvent(row) }; } catch (error) { if (data.id === null) return { done: true, value: undefined }; closeWatcher(data, true); throw error; } }, return() { closeWatcher(data, true); return promiseResolve({ done: true, value: undefined }); }, [asyncIterator]() { return this; }, }; } } define(FsWatcher.prototype, Symbol.toStringTag, { value: "FsWatcher", configurable: true, }); function watchPaths(value: unknown): string[] { const filenames = args(); if (typeof value === "string") { filenames[0] = path(value); return filenames; } let href; try { href = urlHref(value); } catch {} if (href !== undefined) { filenames[0] = path(value); return filenames; } if (!isObject(value)) { throw new TypeError("Expected a filesystem path or iterable of paths"); } const method = value[SymbolIterator]; if (typeof method !== "function") { throw new TypeError("Expected a filesystem path or iterable of paths"); } const iterator = apply(method, value, args()) as { next?: unknown } | null, next = iterator?.next; if (!isObject(iterator) || typeof next !== "function") { throw new TypeError("Invalid filesystem path iterator"); } let count = 0; while (true) { const step = apply(next, iterator, args()); if (!isObject(step)) { throw new TypeError("Invalid filesystem path iterator result"); } if (step.done) break; if (count >= 128) { throw new RangeError("Too many filesystem watch paths"); } filenames[count++] = path(step.value); } if (count === 0) { throw new TypeError("Expected at least one filesystem watch path"); } return filenames; } function watchFs(paths: unknown, options: unknown = undefined): FsWatcher { required(arguments.length, 1); const filenames = watchPaths(paths), d: Record = options === undefined ? ObjectCreate(null) : dictionary(options); const recursiveValue = d.recursive, signal = d.signal; if (recursiveValue !== undefined && typeof recursiveValue !== "boolean") { throw new TypeError("recursive must be boolean"); } if (signal !== undefined && state(signals, signal).aborted) { throw state(signals, signal).reason; } const current = result( watchOp(args(filenames, recursiveValue !== false)), ); const watcher = ObjectCreate(FsWatcher.prototype) as FsWatcher; const data: WatchState = { id: current, owner: watcher, referenced: true, epoch: 0, signal: undefined as unknown, onAbort: null as (() => void) | null, }; set(watchers, watcher, data); register(watchRegistry, watcher, current, watcher); keepAlive(data, 0); if (signal !== undefined) { data.signal = signal; data.onAbort = () => closeWatcher(data, true); addListener(signal, "abort", data.onAbort, { once: true }); if (state(signals, signal).aborted) { const reason = state(signals, signal).reason; closeWatcher(data, true); throw reason; } } return watcher; } function close( data: HandleState, quiet = false, source: "read" | "write" | null = null, ): void { if (data.id === null) { if (!quiet) id(data); return; } const current = data.id; data.id = null; unregister(registry, data.owner); const error = failure("BadResource", "File handle is closed"); if (data.reader && source !== "read") errorStream(data.reader, error); if (data.writer && source !== "write") writableError(data.writer, error); result(closeOp(args(current, quiet))); } class FileHandle { declare static readonly [Symbol.hasInstance]: (value: unknown) => value is FileHandle; constructor() { throw new TypeError("Illegal constructor"); } async read(buffer: Uint8Array): Promise { const data = state(handles, this), current = id(data); bytes(buffer); setBytes(buffer, empty); const count = length(buffer), chunk = result(await safePromise(readOp(args(current, count)))); setBytes(buffer, empty); if (chunk === null) return null; setBytes(buffer, chunk); return length(chunk); } async write(buffer: Uint8Array): Promise { const current = id(state(handles, this)); const snapshot = new Uint8Array(segment(bytes(buffer), 0)); return result(await safePromise(writeOp(args(current, snapshot)))); } async writev(buffers: readonly Uint8Array[]): Promise { const current = id(state(handles, this)); const snapshot = vectors(buffers); return result(await safePromise(writevOp(args(current, snapshot)))); } async seek(offset: number, whence: number): Promise { const current = id(state(handles, this)); integer(offset, true); if (whence !== 0 && whence !== 1 && whence !== 2) { throw new TypeError("Invalid seek mode"); } return result(await safePromise(seekOp(args(current, offset, whence)))); } async stat() { return metadata( result(await safePromise(statOp(id(state(handles, this))))), ); } async truncate(length = 0) { result( await safePromise( truncateOp(args(id(state(handles, this)), integer(length))), ), ); } async sync() { result(await safePromise(syncOp(args(id(state(handles, this)), false)))); } async syncData() { result(await safePromise(syncOp(args(id(state(handles, this)), true)))); } async fchmod(fileMode: number): Promise { result( await safePromise( fchmodOp(args(id(state(handles, this)), mode(fileMode))), ), ); } async fchown( uid: number | null | undefined, gid: number | null | undefined, ): Promise { result( await safePromise( fchownOp(args(id(state(handles, this)), owner(uid), owner(gid))), ), ); } async futime(atime: number | Date, mtime: number | Date): Promise { const before = stamp(atime), after = stamp(mtime); result( await safePromise( futimeOp( args( id(state(handles, this)), before[0], before[1], after[0], after[1], ), ), ), ); } async flock(exclusive = true) { if (exclusive !== true && exclusive !== false) { throw new TypeError("Lock mode must be boolean"); } // Waiting releases the handle queue, so a lock no longer serializes with // reads and writes: fire-and-forget flock();write() runs unordered, and a // close() landing in the same tick must not observe success past close. result( await safePromise(flockOp(args(id(state(handles, this)), exclusive))), ); id(state(handles, this)); } async funlock() { result(await safePromise(funlockOp(id(state(handles, this))))); } readSync(buffer: Uint8Array): number | null { const data = state(handles, this), current = id(data); bytes(buffer); setBytes(buffer, empty); const count = length(buffer), chunk = result(readSyncOp(args(current, count))); setBytes(buffer, empty); if (chunk === null) return null; setBytes(buffer, chunk); return length(chunk); } writeSync(buffer: Uint8Array): number { const current = id(state(handles, this)); const snapshot = new Uint8Array(segment(bytes(buffer), 0)); return result(writeSyncOp(args(current, snapshot))); } writevSync(buffers: readonly Uint8Array[]): number { const current = id(state(handles, this)); const snapshot = vectors(buffers); return result(writevSyncOp(args(current, snapshot))); } seekSync(offset: number, whence: number): number { const current = id(state(handles, this)); integer(offset, true); if (whence !== 0 && whence !== 1 && whence !== 2) { throw new TypeError("Invalid seek mode"); } return result(seekSyncOp(args(current, offset, whence))); } statSync() { return metadata( result(statSyncOp(id(state(handles, this)))), ); } truncateSync(length = 0) { result( truncateSyncOp(args(id(state(handles, this)), integer(length))), ); } syncSync() { result(syncSyncOp(args(id(state(handles, this)), false))); } syncDataSync() { result(syncSyncOp(args(id(state(handles, this)), true))); } fchmodSync(fileMode: number): void { result( fchmodSyncOp(args(id(state(handles, this)), mode(fileMode))), ); } fchownSync( uid: number | null | undefined, gid: number | null | undefined, ): void { result( fchownSyncOp(args(id(state(handles, this)), owner(uid), owner(gid))), ); } futimeSync(atime: number | Date, mtime: number | Date): void { const before = stamp(atime), after = stamp(mtime); result( futimeSyncOp( args( id(state(handles, this)), before[0], before[1], after[0], after[1], ), ), ); } flockSync(exclusive = true) { if (exclusive !== true && exclusive !== false) { throw new TypeError("Lock mode must be boolean"); } result( flockSyncOp(args(id(state(handles, this)), exclusive)), ); id(state(handles, this)); } funlockSync() { result(funlockSyncOp(id(state(handles, this)))); } close() { close(state(handles, this)); } [dispose]() { close(state(handles, this), true); } get readable() { const data = state(handles, this); if (data.readable) return data.readable; id(data); data.readable = new ReadableStream({ type: "bytes", start(controller) { data.reader = controller; }, async pull(controller) { try { const request = byobRequest(controller), view = request && getView(request); const chunk = result | null>( await safePromise( readOp(args(id(data), view ? length(view) : 65536)), ), ); if (data.id === null) return; if (chunk === null) { finish(controller); close(data, true, "read"); } else if (request) { setBytes(view as Uint8Array, chunk); respond(request, length(chunk)); } else enqueue(controller, chunk); } catch (error) { errorStream(controller, error); close(data, true, "read"); } }, cancel() { close(data, true, "read"); }, }, { highWaterMark: 0 }); return data.readable; } get writable() { const data = state(handles, this); if (data.writable) return data.writable; id(data); data.writable = new WritableStream({ start(controller) { data.writer = controller; }, async write(buffer: unknown) { try { const snapshot = new Uint8Array(bytes(buffer)); let offset = 0; do { const count = result( await safePromise( writeOp( args( id(data), segment(snapshot, offset) as Uint8Array, ), ), ), ); if ( !safeInteger(count) || count < 0 || count > length(snapshot) - offset || (count === 0 && offset < length(snapshot)) ) throw failure("IoError", "Invalid partial write"); offset += count; } while (offset < length(snapshot)); } catch (error) { close(data, true, "write"); throw error; } }, close() { close(data, true, "write"); }, abort() { close(data, true, "write"); }, }, { highWaterMark: 65536, size(chunk) { return tag(chunk) === "Uint8Array" ? length(chunk) : 1; }, }); return data.writable; } } const respond = uncurryThis(ReadableStreamBYOBRequest.prototype.respond); define(FileHandle.prototype, Symbol.toStringTag, { value: "FileHandle", configurable: true, }); function openArgs( pathname: unknown, options: unknown, ): [ string, [boolean, boolean, boolean, boolean, boolean, boolean, number | null], ] { const filename = path(pathname), d = options === undefined ? { read: true } : dictionary(options); const flags: (boolean | number | null)[] = array(); const keys = ["read", "write", "append", "truncate", "create", "createNew"]; for (let index = 0; index < keys.length; index++) { const key = keys[index], value = d[key]; if (value !== undefined && typeof value !== "boolean") { throw new TypeError("Open flags must be boolean"); } flags[flags.length] = value === true; } const mode = d.mode; if (mode !== undefined && integer(mode) > 0o7777) { throw new RangeError("Invalid file creation mode"); } flags[flags.length] = mode === undefined ? null : mode as number; return args( filename, flags as [ boolean, boolean, boolean, boolean, boolean, boolean, number | null, ], ); } function writeOptions(options: unknown): Record { const input = dictionary(options), output: Record = ObjectCreate(null); const keys = ["append", "create", "createNew"]; for (let index = 0; index < keys.length; index++) { const key = keys[index], value = input[key]; if (value !== undefined && typeof value !== "boolean") { throw new TypeError("Write flags must be boolean"); } output[key] = value === undefined ? key === "create" : value; } const mode = input.mode; if (mode !== undefined && integer(mode) > 0o7777) { throw new RangeError("Invalid file creation mode"); } output.mode = mode; output.write = true; output.truncate = !output.append; return output; } function fileHandle(current: number): FileHandle { const handle = ObjectCreate(FileHandle.prototype) as FileHandle; set(handles, handle, { id: current, owner: handle, readable: null, writable: null, reader: null, writer: null, }); register(registry, handle, current, handle); return handle; } async function open( pathname: unknown, options: unknown = undefined, ): Promise { required(arguments.length, 1); return fileHandle( result(await safePromise(openOp(openArgs(pathname, options)))), ); } async function create(pathname: unknown): Promise { required(arguments.length, 1); return await safePromise(open(pathname, { read: true, write: true, create: true, truncate: true, })); } async function stat(pathname: unknown) { required(arguments.length, 1); return metadata( result(await safePromise(pathStatOp(args(path(pathname), true)))), ); } async function lstat(pathname: unknown) { required(arguments.length, 1); return metadata( result(await safePromise(pathStatOp(args(path(pathname), false)))), ); } async function* readDir( pathname: unknown, ): AsyncGenerator< { name: string; isFile: boolean; isDirectory: boolean; isSymlink: boolean } > { required(arguments.length, 1); const entries = result(await safePromise(readDirOp(path(pathname)))); for (let i = 0; i < entries.length; i++) { const row = entries[i]; yield { name: row[0], isFile: row[1], isDirectory: row[2], isSymlink: row[3], }; } } async function mkdir( pathname: unknown, options: unknown = undefined, ): Promise { required(arguments.length, 1); const filename = path(pathname), mode = dictionary(options).mode; if (mode !== undefined && integer(mode) > 0o7777) { throw new RangeError("Invalid directory mode"); } result( await safePromise( mkdirOp(args(filename, mode === undefined ? null : mode as number)), ), ); } async function remove( pathname: unknown, options: unknown = undefined, ): Promise { required(arguments.length, 1); const filename = path(pathname), recursive = dictionary(options).recursive; if (recursive !== undefined && typeof recursive !== "boolean") { throw new TypeError("recursive must be boolean"); } result(await safePromise(removeOp(args(filename, recursive === true)))); } async function rename(source: unknown, destination: unknown): Promise { required(arguments.length, 2); result(await safePromise(renameOp(args(path(source), path(destination))))); } async function copyFile(source: unknown, destination: unknown): Promise { required(arguments.length, 2); result(await safePromise(copyOp(args(path(source), path(destination))))); } async function symlink( target: unknown, linkPath: unknown, options: unknown = undefined, ): Promise { required(arguments.length, 2); const contents = path(target), link = path(linkPath), kind = dictionary(options).type; if (kind !== undefined && kind !== "file" && kind !== "dir") { throw new TypeError("Symlink type must be file or dir"); } result( await safePromise( symlinkOp(args(contents, link, kind === undefined ? null : kind)), ), ); } async function readLink(pathname: unknown): Promise { required(arguments.length, 1); return result(await safePromise(readLinkOp(path(pathname)))); } async function realPath(pathname: unknown): Promise { required(arguments.length, 1); return result(await safePromise(realPathOp(path(pathname)))); } async function chmod(pathname: unknown, fileMode: number): Promise { required(arguments.length, 2); result(await safePromise(chmodOp(args(path(pathname), mode(fileMode))))); } async function chown( pathname: unknown, uid: number | null | undefined, gid: number | null | undefined, ): Promise { required(arguments.length, 3); result( await safePromise(chownOp(args(path(pathname), owner(uid), owner(gid)))), ); } async function lchown( pathname: unknown, uid: number | null | undefined, gid: number | null | undefined, ): Promise { required(arguments.length, 3); result( await safePromise(lchownOp(args(path(pathname), owner(uid), owner(gid)))), ); } async function utime( pathname: unknown, atime: number | Date, mtime: number | Date, ): Promise { required(arguments.length, 3); const before = stamp(atime), after = stamp(mtime); result( await safePromise( utimeOp( args(path(pathname), before[0], before[1], after[0], after[1]), ), ), ); } async function lutime( pathname: unknown, atime: number | Date, mtime: number | Date, ): Promise { required(arguments.length, 3); const before = stamp(atime), after = stamp(mtime); result( await safePromise( lutimeOp( args(path(pathname), before[0], before[1], after[0], after[1]), ), ), ); } async function link(oldpath: unknown, newpath: unknown): Promise { required(arguments.length, 2); result(await safePromise(linkOp(args(path(oldpath), path(newpath))))); } async function truncate(pathname: unknown, length = 0): Promise { required(arguments.length, 1); result( await safePromise(truncatePathOp(args(path(pathname), integer(length)))), ); } export { chmod, chown, copyFile, create, errors, FileHandle, FsWatcher, lchown, link, lstat, lutime, mkdir, open, readDir, readLink, realPath, remove, rename, SeekMode, stat, symlink, truncate, utime, watchFs, }; export function processHandle(value: unknown): number { return id(state(handles, value)); } export function responseHandle(value: unknown): number | null { const data = get(handles, value); return data === undefined ? null : id(data); } export function detachResponseHandle(value: unknown): void { close(state(handles, value), true); } export { args, fileHandle, integer, metadata, mode, openArgs, owner, path, stamp, writeOptions, };