UNPKG

@ayonli/jsext

Version:

A JavaScript extension package for building strong and modern applications.

920 lines (919 loc) 28.4 kB
/** * Universal file system APIs for both server and browser applications. * * This module is guaranteed to work in the following environments: * * - Node.js * - Deno * - Bun * - Modern browsers * - Cloudflare Workers (limited support and experimental) * * We can also use the {@link runtime} function to check whether the runtime * has file system support. When `runtime().fsSupport` is `true`, this module * should work properly. * * In most browsers, this module uses the * [Origin Private File System](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system). * In Chromium browsers, this module can also access the device's local file * system via `window.showOpenFilePicker()` and `window.showDirectoryPicker()`. * * This module also provides limited support for Cloudflare Workers, however it * requires setting the `[site].bucket` option in the `wrangler.toml` file. Only * the reading functions are supported, such as {@link readFile} and * {@link readDir}, these functions allow us reading static files in the workers, * writing functions is not implemented at the moment. More details about * serving static assets in Cloudflare Workers can be found here: * [Add static assets to an existing Workers project](https://developers.cloudflare.com/workers/configuration/sites/start-from-worker/). * * **Errors:** * * When a file system operation fails, this module throws an {@link Exception} * with one of the following names: * * - `NotFoundError`: The file or directory does not exist. * - `NotAllowedError`: The operation is not allowed, such as being blocked by * the permission system. * - `AlreadyExistsError`: The file or directory already exists. * - `IsDirectoryError`: The path is a directory, not a file. * - `NotDirectoryError`: The path is a file, not a directory. * - `InvalidOperationError`: The operation is not supported, such as trying to * copy a directory without the `recursive` option. * - `BusyError`: The file is busy, such as being locked by another program. * - `InterruptedError`: The operation is interrupted by the underlying file * system. * - `FileTooLargeError`: The file is too large, or the file system doesn't have * enough space to store the new content. * - `FilesystemLoopError`: Too many symbolic links were encountered when * resolving the filename. * * Other errors may also be thrown by the runtime, such as `TypeError`. * * @experimental * @module */ import type { FileInfo, DirEntry, FileSystemOptions, DirTree } from "./fs/types.ts"; export type { FileSystemOptions, FileInfo, DirEntry, DirTree }; /** * @deprecated Use {@link FileSystemOptions} instead. */ export type CommonOptions = FileSystemOptions; /** * Platform-specific end-of-line marker. The value is `\r\n` in Windows * server-side environments, and `\n` elsewhere. */ export declare const EOL: "\n" | "\r\n"; /** * Options for the {@link getDirHandle} function. */ export interface GetDirOptions extends FileSystemOptions { /** * Create the directory if not exist. */ create?: boolean; /** * Used when `create` is `true`, recursively create the directory and its * parent directories. */ recursive?: boolean; } /** * Obtains the directory handle of the given path. * * NOTE: This function is only available in the browser. * * NOTE: If the `path` is not provided or is empty, the root directory handle * will be returned. * * @example * ```ts * // with the default storage * import { getDirHandle } from "@ayonli/jsext/fs"; * * const dir = await getDirHandle("/path/to/dir"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { getDirHandle } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * const dir = await getDirHandle("/path/to/dir", { root }); * ``` * * @example * ```ts * // create the directory if not exist * import { getDirHandle } from "@ayonli/jsext/fs"; * * const dir = await getDirHandle("/path/to/dir", { create: true, recursive: true }); * ``` * * @example * ```ts * // return the root directory handle * import { getDirHandle } from "@ayonli/jsext/fs"; * * const root = await getDirHandle(); * ``` */ export declare function getDirHandle(path?: string, options?: GetDirOptions): Promise<FileSystemDirectoryHandle>; /** * Options for the {@link getFileHandle} function. */ export interface GetFileOptions extends FileSystemOptions { /** * Create the file if not exist. */ create?: boolean; } /** * Obtains the file handle of the given path. * * NOTE: This function is only available in the browser. * * @example * ```ts * // with the default storage * import { getFileHandle } from "@ayonli/jsext/fs"; * * const file = await getFileHandle("/path/to/file.txt"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { getFileHandle } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * const file = await getFileHandle("/path/to/file.txt", { root }); * ``` * * @example * ```ts * // create the file if not exist * import { getFileHandle } from "@ayonli/jsext/fs"; * * const file = await getFileHandle("/path/to/file.txt", { create: true }); * ``` */ export declare function getFileHandle(path: string, options?: GetFileOptions): Promise<FileSystemFileHandle>; /** * Checks if the given path exists. * * This function may throw an error if the path is invalid or the operation is * not allowed. * * NOTE: This function can also be used in Cloudflare Workers. * * @example * ```ts * // with the default storage * import { exists } from "@ayonli/jsext/fs"; * * if (await exists("/path/to/file.txt")) { * console.log("The file exists."); * } else { * console.log("The file does not exist."); * } * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { exists } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * * if (await exists("/path/to/file.txt", { root })) { * console.log("The file exists."); * } else { * console.log("The file does not exist."); * } * ``` */ export declare function exists(path: string, options?: FileSystemOptions): Promise<boolean>; /** * Options for the {@link stat} function. */ export interface StatOptions extends FileSystemOptions { /** * Whether to follow the symbolic link. * @default false */ followSymlink?: boolean; } /** * Returns the information of the given file or directory. * * NOTE: This function can also be used in Cloudflare Workers. * * @example * ```ts * // with the default storage * import { stat } from "@ayonli/jsext/fs"; * * const info = await stat("/path/to/file.txt"); * console.log(`${info.name} is a ${info.kind}, its size is ${info.size} bytes, with MIME type ${info.type}.`); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { stat } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * const info = await stat("/path/to/file.txt", { root }); * console.log(`${info.name} is a ${info.kind}, its size is ${info.size} bytes, with MIME type ${info.type}.`); * ``` */ export declare function stat(target: string | FileSystemFileHandle | FileSystemDirectoryHandle, options?: StatOptions): Promise<FileInfo>; /** * Options for the {@link mkdir} function. */ export interface MkdirOptions extends FileSystemOptions { /** * Whether to create parent directories if they do not exist. */ recursive?: boolean; /** * The permission mode of the directory. * * NOTE: This option is ignored in the browser and in Windows. * @default 0o777 */ mode?: number; } /** * Creates a new directory with the given path. * * @example * ```ts * // with the default storage * import { mkdir } from "@ayonli/jsext/fs"; * * await mkdir("/path/to/dir"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { mkdir } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * await mkdir("/path/to/dir", { root }); * ``` * * @example * ```ts * // create the directory and its parent directories if not exist * import { mkdir } from "@ayonli/jsext/fs"; * * await mkdir("/path/to/dir", { recursive: true }); * ``` */ export declare function mkdir(path: string, options?: MkdirOptions): Promise<void>; /** * Ensures the directory exists, creating it (and any parent directory) if not. * * @example * ```ts * // with the default storage * import { ensureDir } from "@ayonli/jsext/fs"; * * await ensureDir("/path/to/dir"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { ensureDir } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * await ensureDir("/path/to/dir", { root }); * ``` */ export declare function ensureDir(path: string, options?: Omit<MkdirOptions, "recursive">): Promise<void>; /** * Options for the {@link readDir} function. */ export interface ReadDirOptions extends FileSystemOptions { /** * Whether to read the sub-directories recursively. */ recursive?: boolean; } /** * Reads the directory of the given path and iterates its entries. * * NOTE: The order of the entries is not guaranteed. * * NOTE: This function can also be used in Cloudflare Workers. * * @example * ```ts * // with the default storage * import { readDir } from "@ayonli/jsext/fs"; * * for await (const entry of readDir("/path/to/dir")) { * console.log(`${entry.name} is a ${entry.kind}, its relative path is '${entry.relativePath}'.`); * } * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { readDir } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * for await (const entry of readDir("/path/to/dir", { root })) { * console.log(`${entry.name} is a ${entry.kind}, its relative path is '${entry.relativePath}'.`); * } * ``` * * @example * ```ts * // read the sub-directories recursively * import { readDir } from "@ayonli/jsext/fs"; * * for await (const entry of readDir("/path/to/dir", { recursive: true })) { * console.log(`${entry.name} is a ${entry.kind}, its relative path is '${entry.relativePath}'.`); * } * ``` */ export declare function readDir(target: string | FileSystemDirectoryHandle, options?: ReadDirOptions): AsyncIterableIterator<DirEntry>; /** * Recursively reads the contents of the directory and transform them into a * tree structure. * * NOTE: Unlike {@link readDir}, the order of the entries returned by this * function is guaranteed, they are ordered first by kind (directories before * files), then by names alphabetically. * * NOTE: This function can also be used in Cloudflare Workers. * * @example * ```ts * // with the default storage * import { readTree } from "@ayonli/jsext/fs"; * * const tree = await readTree("/path/to/dir"); * console.log(tree); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { readTree } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * const tree = await readTree("/path/to/dir", { root }); * console.log(tree); * ``` */ export declare function readTree(target: string | FileSystemDirectoryHandle, options?: FileSystemOptions): Promise<DirTree>; /** * Options for file reading functions, such as {@link readFile}, * {@link readFileAsText} and {@link readFileAsFile}. */ export interface ReadFileOptions extends FileSystemOptions { signal?: AbortSignal; } /** * Reads the content of the given file in bytes. * * NOTE: This function can also be used in Cloudflare Workers. * * @example * ```ts * // with the default storage * import { readFile } from "@ayonli/jsext/fs"; * * const bytes = await readFile("/path/to/file.txt"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { readFile } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * const bytes = await readFile("/path/to/file.txt", { root }); * ``` */ export declare function readFile(target: string | FileSystemFileHandle, options?: ReadFileOptions): Promise<Uint8Array>; /** * Reads the content of the given file as text with `utf-8` encoding. * * NOTE: This function can also be used in Cloudflare Workers. * * @example * ```ts * // with the default storage * import { readFileAsText } from "@ayonli/jsext/fs"; * * const text = await readFileAsText("/path/to/file.txt"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { readFileAsText } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * const text = await readFileAsText("/path/to/file.txt", { root }); * ``` */ export declare function readFileAsText(target: string | FileSystemFileHandle, options?: ReadFileOptions): Promise<string>; /** * Reads the file as a `File` object. * * NOTE: This function can also be used in Cloudflare Workers. * * @example * ```ts * // with the default storage * import { readFileAsFile } from "@ayonli/jsext/fs"; * * const file = await readFileAsFile("/path/to/file.txt"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { readFileAsFile } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * const file = await readFileAsFile("/path/to/file.txt", { root }); * ``` */ export declare function readFileAsFile(target: string | FileSystemFileHandle, options?: ReadFileOptions): Promise<File>; /** * Options for file writing functions, such as {@link writeFile} and {@link writeLines}. */ export interface WriteFileOptions extends FileSystemOptions { /** * Append the data to the file instead of overwriting it. */ append?: boolean; /** * Permissions always applied to file. * * NOTE: This option is ignored in the browser. * @default 0o666 */ mode?: number; signal?: AbortSignal; } /** * Writes the given data to the file. * * @example * ```ts * // with the default storage * import { writeFile } from "@ayonli/jsext/fs"; * * await writeFile("/path/to/file.txt", "Hello, world!"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { writeFile } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * await writeFile("/path/to/file.txt", "Hello, world!", { root }); * ``` * * @example * ```ts * // append the data to the file * import { writeFile } from "@ayonli/jsext/fs"; * * await writeFile("/path/to/file.txt", "Hello, world!", { append: true }); * ``` * * @example * ```ts * // write binary data to the file * import { writeFile } from "@ayonli/jsext/fs"; * import bytes from "@ayonli/jsext/bytes"; * * const data = bytes("Hello, world!"); * await writeFile("/path/to/file.txt", data) * ``` * * @example * ```ts * // write a blob to the file * import { writeFile } from "@ayonli/jsext/fs"; * * const blob = new Blob(["Hello, world!"], { type: "text/plain" }); * await writeFile("/path/to/file.txt", blob); * ``` * * @example * ```ts * // write a readable stream to the file * import { writeFile } from "@ayonli/jsext/fs"; * * const res = await fetch("https://example.com/file.txt"); * await writeFile("/path/to/file.txt", res.body!); * ``` */ export declare function writeFile(target: string | FileSystemFileHandle, data: string | ArrayBuffer | ArrayBufferView | ReadableStream<Uint8Array> | Blob, options?: WriteFileOptions): Promise<void>; /** * Writes multiple lines of content to the file. * * This function will automatically detect the line ending of the current * content and use it to write the new lines. If the file is empty or does not * exists (will be created automatically), it will use the system's default line * ending to separate lines. * * This function will append a new line at the end of the final content, in * appending mode, it will also prepend a line ending before the input lines if * the current content doesn't ends with one. * * @example * ```ts * // with the default storage * import { writeLines } from "@ayonli/jsext/fs"; * * await writeLines("/path/to/file.txt", ["Hello", "World"]); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { writeLines } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * await writeLines("/path/to/file.txt", ["Hello", "World"], { root }); * ``` * * @example * ```ts * // append the lines to the file * import { writeLines } from "@ayonli/jsext/fs"; * * await writeLines("/path/to/file.txt", ["Hello", "World"], { append: true }); * ``` */ export declare function writeLines(target: string | FileSystemFileHandle, lines: string[], options?: WriteFileOptions): Promise<void>; /** * Truncates (or extends) the file to reach the specified `size`. If `size` is * not specified then the entire file contents are truncated. * * @example * ```ts * // with the default storage * import { stat, truncate } from "@ayonli/jsext/fs"; * * await truncate("/path/to/file.txt", 1024); * const info = await stat("/path/to/file.txt"); * console.assert(info.size === 1024); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { stat, truncate } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * await truncate("/path/to/file.txt", 1024, { root }); * const info = await stat("/path/to/file.txt", { root }); * console.assert(info.size === 1024); * ``` * * @example * ```ts * // truncate the file to zero size * import { stat, truncate } from "@ayonli/jsext/fs"; * * await truncate("/path/to/file.txt"); * const info = await stat("/path/to/file.txt"); * console.assert(info.size === 0); * ``` */ export declare function truncate(target: string | FileSystemFileHandle, size?: number, options?: FileSystemOptions): Promise<void>; /** * Options for the {@link remove} function. */ export interface RemoveOptions extends FileSystemOptions { /** * Whether to delete the sub-directories and files recursively. This option * is required in order to remove a non-empty directory. */ recursive?: boolean; } /** * Removes the file or directory of the given path from the file system. * * @example * ```ts * // with the default storage * import { remove } from "@ayonli/jsext/fs"; * * await remove("/path/to/file.txt"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { remove } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * await remove("/path/to/file.txt", { root }); * ``` * * @example * ```ts * // remove the directory and its contents recursively * import { remove } from "@ayonli/jsext/fs"; * * await remove("/path/to/dir", { recursive: true }); * ``` */ export declare function remove(path: string, options?: RemoveOptions): Promise<void>; /** * Renames the file or directory from the old path to the new path. * * @example * ```ts * // with the default storage * import { rename } from "@ayonli/jsext/fs"; * * await rename("/path/to/old.txt", "/path/to/new.txt"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { rename } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * await rename("/path/to/old.txt", "/path/to/new.txt", { root }); * ``` */ export declare function rename(oldPath: string, newPath: string, options?: FileSystemOptions): Promise<void>; /** * Options for the {@link copy} function. */ export interface CopyOptions extends FileSystemOptions { /** * Whether to copy the directory recursively, which means to copy the * directory and all its contents. */ recursive?: boolean; } /** * Copies the file or directory (and its contents) from the old location to the * new location. * * NOTE: If the old location is a file and the new location is a directory, the * file will be copied into the new directory with the old name. * * NOTE: In Unix/Linux systems, when using the `cp -R` command to copy a path * without an ending slash, the command will copy the directory itself into the * new path if the new path already exists. This function does not have this * behavior, it does not distinguish between a path with a trailing slash and a * path without it. So when copying a directory, this function always copy its * contents to the new path, whether the new path already exists or not. * * @example * ```ts * // with the default storage * import { copy } from "@ayonli/jsext/fs"; * * await copy("/path/to/old.txt", "/path/to/new.txt"); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { copy } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * await copy("/path/to/old.txt", "/path/to/new.txt", { root }); * ``` * * @example * ```ts * // copy a directory and its contents recursively * import { copy } from "@ayonli/jsext/fs"; * * await copy("/path/to/dir", "/path/to/new", { recursive: true }); * ``` * * @example * ```ts * // copy a file to a directory * import { copy, exists } from "@ayonli/jsext/fs"; * * await copy("/path/to/file.txt", "/path/to/dir"); * console.assert(await exists("/path/to/dir/file.txt")); * ``` */ export declare function copy(src: string, dest: string, options?: CopyOptions): Promise<void>; /** * @example * ```ts * // copy a file from the device's file system to the browser's file system (Chromium only) * import { copy, getFileHandle } from "@ayonli/jsext/fs"; * * const file1 = await window.showOpenFilePicker(); * const file2 = await getFileHandle("/path/to/file.txt"); * * await copy(file1[0], file2); * ``` */ export declare function copy(src: FileSystemFileHandle, dest: FileSystemFileHandle | FileSystemDirectoryHandle): Promise<void>; /** * @example * ```ts * // copy a directory from the device's file system to the browser's file system (Chromium only) * import { copy, getDirHandle } from "@ayonli/jsext/fs"; * * const dir1 = await window.showDirectoryPicker(); * const dir2 = await getDirHandle("/path/to/dir"); * * await copy(dir1, dir2, { recursive: true }); * ``` */ export declare function copy(src: FileSystemDirectoryHandle, dest: FileSystemDirectoryHandle, options?: Pick<CopyOptions, "recursive">): Promise<void>; /** * Options for the {@link link} function. */ export interface LinkOptions { /** * Create a symbolic link instead of a hard link. */ symbolic?: boolean; } /** * Creates a hard link (or symbolic link) from the source path to the destination * path. * * NOTE: This function is only available in Node.js, Deno and Bun. * * @example * ```ts * // create a hard link * import { link } from "@ayonli/jsext/fs"; * * await link("/path/to/file.txt", "/path/to/link.txt"); * ``` * * @example * ```ts * // create a symbolic link * import { link } from "@ayonli/jsext/fs"; * * await link("/path/to/file.txt", "/path/to/link.txt", { symbolic: true }); * ``` */ export declare function link(src: string, dest: string, options?: LinkOptions): Promise<void>; /** * Returns the destination path of a symbolic link. * * NOTE: This function is only available in Node.js, Deno and Bun. * * @example * ```ts * import { readLink } from "@ayonli/jsext/fs"; * * const dest = await readLink("/path/to/link.txt"); * console.log(dest); * ``` */ export declare function readLink(path: string): Promise<string>; /** * Changes the permission of the specified file or directory. * * The mode is a sequence of 3 octal numbers. The first/left-most number * specifies the permissions for the owner. The second number specifies the * permissions for the group. The last/right-most number specifies the * permissions for others. For example, with a mode of 0o764, the owner (7) can * read/write/execute, the group (6) can read/write and everyone else (4) can * read only. * * | Number | Description | * | ------ | ----------- | * | 7 | read, write, and execute | * | 6 | read and write | * | 5 | read and execute | * | 4 | read only | * | 3 | write and execute | * | 2 | write only | * | 1 | execute only | * | 0 | no permission | * * NOTE: This function is only available in Node.js, Deno and Bun, and only * works in Unix/Linux systems, in other environments, it's a no-op. * * @example * ```ts * import { chmod } from "@ayonli/jsext/fs"; * * // Change the file's permission to read/write for owner, read for group and others. * await chmod("/path/to/file.txt", 0o644); * ``` */ export declare function chmod(path: string, mode: number): Promise<void>; /** * Changes the owner and group of the specified file or directory. * * NOTE: This function is only available in Node.js, Deno and Bun, and only * works in Unix/Linux systems, in other environments, it's a no-op. * * @example * ```ts * import { chown } from "@ayonli/jsext/fs"; * * // Change the owner and group of the file to root. * await chown("/path/to/file.txt", 0, 0); * ``` */ export declare function chown(path: string, uid: number, gid: number): Promise<void>; /** * Changes the access (`atime`) and modification (`mtime`) times of the file * or directory. Given times are either in seconds (UNIX epoch time) or as `Date` * objects. * * NOTE: This function only works in Node.js, Deno and Bun, in other * environments, it's a no-op. * * @example * ```ts * import { utimes } from "@ayonli/jsext/fs"; * * // Set the access and modification times to the current time. * await utimes("/path/to/file.txt", Date.now(), Date.now()); * ``` */ export declare function utimes(path: string, atime: number | Date, mtime: number | Date): Promise<void>; /** * Creates a readable stream for the target file. * * NOTE: In Node.js, this function requires Node.js v18.0 or above. * * @example * ```ts * // with the default storage * import { createReadableStream } from "@ayonli/jsext/fs"; * import { readAsText } from "@ayonli/jsext/reader"; * * const input = createReadableStream("/path/to/file.txt"); * * const text = await readAsText(input); * console.log(text); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { createReadableStream } from "@ayonli/jsext/fs"; * import { readAsText } from "@ayonli/jsext/reader"; * * const root = await window.showDirectoryPicker(); * const input = createReadableStream("/path/to/file.txt", { root }); * * const text = await readAsText(input); * console.log(text); * ``` */ export declare function createReadableStream(target: string | FileSystemFileHandle, options?: FileSystemOptions): ReadableStream<Uint8Array>; /** * @deprecated use `createReadableStream` instead. */ export declare const readFileAsStream: typeof createReadableStream; /** * Creates a writable stream for the target file. * * NOTE: In Node.js, this function requires Node.js v18.0 or above. * * @example * ```ts * // with the default storage * import { createWritableStream } from "@ayonli/jsext/fs"; * * const output = createWritableStream("/path/to/file.txt"); * const res = await fetch("https://example.com/file.txt"); * * await res.body!.pipeTo(output); * ``` * * @example * ```ts * // with a user-selected directory as root (Chromium only) * import { createWritableStream } from "@ayonli/jsext/fs"; * * const root = await window.showDirectoryPicker(); * const output = createWritableStream("/path/to/file.txt", { root }); * const res = await fetch("https://example.com/file.txt"); * * await res.body!.pipeTo(output); * ``` */ export declare function createWritableStream(target: string | FileSystemFileHandle, options?: Omit<WriteFileOptions, "signal">): WritableStream<Uint8Array>;