Repository navigation
docs, bun-types: describe how Bun.write and FileSink treat an existing file, accept a file descriptor number #41745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9bcbf78
17c8f09
6ac3de7
ffa7836
5e037c9
67aafbd
eb88455
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2140,20 +2140,37 @@ declare module "bun" { | |
| * | ||
| * If `destination` exists, it must be a regular file or symlink to a file. If `destination`'s directory does not exist, it is created by default. | ||
| * | ||
| * An existing file is overwritten in place and truncated to the new length. It keeps its inode and hard links, and | ||
| * it keeps its permissions unless `mode` sets them. A file descriptor destination (a number or `Bun.file(fd)`) is | ||
| * written at its current position and is not truncated, except that an empty `input` that is not a file or a | ||
| * stream truncates it to zero length. | ||
| * | ||
| * The write is not atomic: concurrent writers to one path can interleave. For an atomic replace, write to a | ||
| * temporary path and `rename()` it over the destination. Writes to one path that overlap are not ordered either: | ||
| * an earlier write can finish after a later one. Start a write to a path after the previous write to it settles. | ||
| * | ||
| * @category File System | ||
| * | ||
| * @param destination The file or file path to write to | ||
| * @param destination The file, file path, or file descriptor to write to | ||
| * @param input The data to copy into `destination` | ||
| * @param options Options for the write | ||
| * | ||
| * @returns A promise that resolves with the number of bytes written. | ||
| */ | ||
| function write( | ||
| destination: BunFile | S3File | PathLike, | ||
| destination: BunFile | S3File | PathLike | number, | ||
| input: Blob | NodeJS.TypedArray | ArrayBufferLike | string | BlobPart[] | Archive | ReadableStream, | ||
| options?: { | ||
| /** | ||
| * If writing to a PathLike, set the permissions of the file. | ||
| * The permissions of the destination file, for example `0o600`. | ||
| * | ||
| * `mode` takes effect when `input` is a `BunFile`: the destination gets | ||
| * exactly this mode, whether Bun creates it or it already exists. When | ||
| * `input` is a `ReadableStream`, Bun uses `mode` only to create a file | ||
| * that does not exist, and the process umask applies. | ||
| * | ||
| * `mode` has no effect when `input` is a string, a `TypedArray`, an | ||
| * `ArrayBuffer`, an `Archive`, or a `Blob` that holds data. | ||
| */ | ||
| mode?: number; | ||
| /** | ||
|
|
@@ -2171,16 +2188,19 @@ declare module "bun" { | |
| * Persist a {@link Response} or {@link Request} body to disk. The body is | ||
| * streamed into the file as it arrives. | ||
| * | ||
| * @param destination The file to write to. If the file doesn't exist, it is | ||
| * created; if it does, it is overwritten. If `input` is smaller than | ||
| * `destination`, `destination` is truncated. | ||
| * @param destination The file or file descriptor to write to. A | ||
| * `Bun.file(path)` that doesn't exist is created. One that exists is | ||
| * overwritten, and truncated if `input` is smaller. A file descriptor (a | ||
| * number or `Bun.file(fd)`) is written at its current position and is not | ||
| * truncated, except that an empty body that is not a file truncates it to | ||
| * zero length. | ||
| * @param input The `Response` or `Request` whose body is written | ||
| * @param options Options for the write | ||
| * | ||
| * @returns A promise that resolves with the number of bytes written. | ||
| */ | ||
| function write( | ||
| destination: BunFile, | ||
| destination: BunFile | number, | ||
| input: Response | Request, | ||
| options?: { | ||
| /** | ||
|
|
@@ -2230,15 +2250,17 @@ declare module "bun" { | |
| * [`clonefile()`](https://www.manpagez.com/man/2/clonefile/) and falls | ||
| * back to [`fcopyfile()`](https://www.manpagez.com/man/2/fcopyfile/). | ||
| * | ||
| * @param destination The file to write to. If the file doesn't exist, it is | ||
| * created; if it does, it is overwritten. If `input` is smaller than | ||
| * `destination`, `destination` is truncated. | ||
| * @param destination The file or file descriptor to write to. A | ||
| * `Bun.file(path)` that doesn't exist is created. One that exists is | ||
| * overwritten, and truncated if `input` is smaller. A file descriptor (a | ||
| * number or `Bun.file(fd)`) is written at its current position and is not | ||
| * truncated. | ||
| * @param input The file to copy from | ||
| * @returns A promise that resolves with the number of bytes written. | ||
| */ | ||
|
|
||
| function write( | ||
| destination: BunFile, | ||
| destination: BunFile | number, | ||
| input: BunFile, | ||
| options?: { | ||
| /** | ||
|
|
@@ -2714,6 +2736,19 @@ declare module "bun" { | |
|
|
||
| /** | ||
| * Incremental writer for files and pipes. | ||
| * | ||
| * For a path-backed `BunFile`, this opens the file immediately and creates | ||
| * it if it does not exist (the parent directory must exist). Writes start | ||
| * at the beginning of the file. There is no append mode: to append, open | ||
| * the file with `fs.openSync(path, "a")` and call `Bun.file(fd).writer()`. | ||
| * The writer does not close a file descriptor that you passed in. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const writer = Bun.file("output.txt").writer(); | ||
| * writer.write("hello\n"); | ||
| * await writer.end(); | ||
| * ``` | ||
| */ | ||
| writer(options?: { highWaterMark?: number }): FileSink; | ||
|
Comment on lines
2737
to
2753
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟣 pre-existing, not blocking: nit, pre-existing: TypeScript users still get a Why this was flaggedA user calls Verification: pre-existing (nit). Trigger: any TypeScript caller writing |
||
|
|
||
|
|
@@ -2759,7 +2794,23 @@ declare module "bun" { | |
| */ | ||
| write( | ||
| data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | ReadableStream, | ||
| options?: { highWaterMark?: number }, | ||
| options?: { | ||
| /** | ||
| * If `true`, create the parent directory if it doesn't exist. | ||
| * | ||
| * If `false`, the write rejects when the directory doesn't exist. | ||
| * | ||
| * @default true | ||
| */ | ||
| createPath?: boolean; | ||
|
Comment on lines
+2798
to
+2805
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 nit (optional), pre-existing: a caller who passes the documented default Why this was flaggedA caller writes Verification: nit. Trigger: a TypeScript caller passes the documented default explicitly, |
||
| /** | ||
| * Set the MIME type of this `BunFile`: the value of its `type` | ||
| * property, and the `Content-Type` of a `Response` made from the file. | ||
| * The file on disk does not record it. A string that is not a valid | ||
| * MIME type is ignored. | ||
| */ | ||
| type?: string; | ||
| }, | ||
|
robobun marked this conversation as resolved.
Resolved
|
||
| ): Promise<number>; | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import type { BunFile } from "bun"; | ||
| import { tmpdirSync } from "harness"; | ||
| import { tempDir, tmpdirSync } from "harness"; | ||
| import { constants, promises as fs } from "node:fs"; | ||
| import path from "node:path"; | ||
|
|
||
|
|
@@ -18,20 +18,20 @@ describe("Bun.write()", () => { | |
| }, | ||
| ); | ||
|
|
||
| // FIXME | ||
| it.failing("Throws when `destination` is a number ", async () => { | ||
| // @ts-expect-error | ||
| await expect(() => Bun.write(1, "foo 1")).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); | ||
| // @ts-expect-error | ||
| await expect(() => Bun.write(0, "foo 0")).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); | ||
| // @ts-expect-error | ||
| await expect(() => Bun.write(-10, "foo -10")).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); | ||
| // A number is a file descriptor. The caller owns it: Bun writes at its position and does not truncate the file. | ||
| it("Writes to the file descriptor when `destination` is a number", async () => { | ||
| using dir = tempDir("bun-write-fd", { "file.txt": "0123456789" }); | ||
| const filepath = path.join(String(dir), "file.txt"); | ||
| const handle = await fs.open(filepath, "r+"); | ||
| try { | ||
| expect(await Bun.write(handle.fd, "abc")).toBe(3); | ||
| } finally { | ||
| await handle.close(); | ||
| } | ||
| expect(await fs.readFile(filepath, "utf-8")).toBe("abc3456789"); | ||
| }); | ||
|
Comment on lines
+22
to
32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 nit (optional): the only runtime test for a number destination writes at position 0 with a string, so it cannot fail if Bun ignored the descriptor's position, and it exercises none of the other overloads this PR adds Why this was flaggedtest/js/bun/bun-object/write.spec.ts:22-32 opens "file.txt" holding "0123456789" with "r+" (position 0), writes "abc" and asserts "abc3456789". A runtime that always wrote at offset 0 and never honoured the descriptor position would pass this test, so the "writes at its position" comment at line 21 is not what the assertion proves; only no-truncation is proved. The PR changes bun.d.ts:2203 and bun.d.ts:2263 so TypeScript now accepts a number for Verification: nit. A future regression in numeric-fd handling (writing at offset 0 instead of the descriptor's position) would pass the suite. test/js/bun/bun-object/write.spec.ts:21-32 seeds "0123456789", opens with |
||
|
|
||
| // NOTE: if/when we ban fds, this will become ERR_INVALID_ARG_TYPE. When that | ||
| // happens, delete or update this test | ||
| it("Throws when given a negative number", () => { | ||
| // @ts-expect-error | ||
| expect(() => Bun.write(-1, "foo")).toThrow(RangeError); | ||
| }); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.