diff options
author | 2023-05-29 11:49:51 -0700 | |
---|---|---|
committer | 2023-05-29 11:49:51 -0700 | |
commit | 9b6913e1a674ceb7f670f917fc355bb8758c6c72 (patch) | |
tree | 9ff0bb4a8c22f8f9505242e5f0e6e40e795da0df /docs/api | |
parent | e2de1f5c133ed3aac6fcea7e8e7c5fcd771d65f9 (diff) | |
download | bun-9b6913e1a674ceb7f670f917fc355bb8758c6c72.tar.gz bun-9b6913e1a674ceb7f670f917fc355bb8758c6c72.tar.zst bun-9b6913e1a674ceb7f670f917fc355bb8758c6c72.zip |
More/better docs for JSX, utils, binary data, streams, hashing, `bun test`, `Bun.serve` (#3005)
* WIP
* Updates
* Document deepEquals
* WIP
* Update typeS
* Update TLS docs for Bun.serve
* Update types for tls
* Draft binary data page. Add Streams page.
* Update test runner docs
* Add hashing, flesh out utils
* Grammar
* Update types
* Fix
* Add import.meta docs
* Tee
Diffstat (limited to 'docs/api')
-rw-r--r-- | docs/api/binary-data.md | 988 | ||||
-rw-r--r-- | docs/api/hashing.md | 139 | ||||
-rw-r--r-- | docs/api/http.md | 83 | ||||
-rw-r--r-- | docs/api/import-meta.md | 52 | ||||
-rw-r--r-- | docs/api/streams.md | 172 | ||||
-rw-r--r-- | docs/api/tcp.md | 33 | ||||
-rw-r--r-- | docs/api/utils.md | 366 |
7 files changed, 1795 insertions, 38 deletions
diff --git a/docs/api/binary-data.md b/docs/api/binary-data.md new file mode 100644 index 000000000..71b02338f --- /dev/null +++ b/docs/api/binary-data.md @@ -0,0 +1,988 @@ +This page is intended as an introduction to working with binary data in JavaScript. Bun implements a number of data types and utilities for working with binary data, most of which are Web-standard. Any Bun-specific APIs will be noted as such. + +Below is a quick "cheat sheet" that doubles as a table of contents. Click an item in the left column to jump to that section. + +{% table %} + +--- + +- [`TypedArray`](#typedarray) +- A family of classes that provide an `Array`-like interface for interacting with binary data. Includes `Uint8Array`, `Uint16Array`, `Int8Array`, and more. + +--- + +- [`Buffer`](#buffer) +- A subclass of `Uint8Array` that implements a wide range of convenience methods. Unlike the other elements in this table, this is a Node.js API (which Bun implements). It can't be used in the browser. + +--- + +- [`DataView`](#dataview) +- A class that provides a `get/set` API for writing some number of bytes to an `ArrayBuffer` at a particular byte offset. Often used reading or writing binary protocols. + +--- + +- [`Blob`](#blob) +- A readonly blob of binary data usually representing a file. Has a MIME `type`, a `size`, and methods for converting to `ArrayBuffer`, `ReadableStream`, and string. + +--- + +<!-- - [`File`](#file) +- _Browser only_. A subclass of `Blob` that represents a file. Has a `name` and `lastModified` timestamp. There is experimental support in Node.js v20; Bun does not support `File` yet; most of its functionality is provided by `BunFile`. + +--- --> + +- [`BunFile`](#bunfile) +- _Bun only_. A subclass of `Blob` that represents a lazily-loaded file on disk. Created with `Bun.file(path)`. + +{% /table %} + +## `ArrayBuffer` and views + +Until 2009, there was no language-native way to store and manipulate binary data in JavaScript. ECMAScript v5 introduced a range of new mechanisms for this. The most fundamental building block is `ArrayBuffer`, a simple data structure that represents a sequence of bytes in memory. + +```ts +// this buffer can store 8 bytes +const buf = new ArrayBuffer(8); +``` + +Despite the name, it isn't an array and supports none of the array methods and operators one might expect. In fact, there is no way to directly read or write values from an `ArrayBuffer`. There's very little you can do with one except check its size and create "slices" from it. + +```ts +const buf = new ArrayBuffer(8); + +buf.byteLength; // => 8 + +const slice = buf.slice(0, 4); // returns new ArrayBuffer +slice.byteLength; // => 4 +``` + +To do anything interesting we need a construct known as a "view". A view is a class that _wraps_ an `ArrayBuffer` instance and lets you read and manipulate the underlying data. There are two types of views: _typed arrays_ and `DataView`. + +### `DataView` + +The `DataView` class is a lower-level interface for reading and manipulating the data in an `ArrayBuffer`. + +Below we create a new `DataView` and set the first byte to 5. + +```ts +const buf = new ArrayBuffer(4); +// [0x0, 0x0, 0x0, 0x0] + +const dv = new DataView(buf); +dv.setUint8(0, 3); // write value 3 at byte offset 0 +dv.getUint8(0); // => 3 +// [0x11, 0x0, 0x0, 0x0] +``` + +Now lets write a `Uint16` at byte offset `1`. This requires two bytes. We're using the value `513`, which is `2 * 256 + 1`; in bytes, that's `00000010 00000001`. + +```ts +dv.setUint16(1, 513); +// [0x11, 0x10, 0x1, 0x0] + +console.log(dv.getUint16(1)); // => 513 +``` + +We've now assigned a value to the first three bytes in our underlying `ArrayBuffer`. Even though the second and third bytes were created using `setUint16()`, we can still read each of its component bytes using `getUint8()`. + +```ts +console.log(dv.getUint8(1)); // => 2 +console.log(dv.getUint8(2)); // => 1 +``` + +Attempting to write a value that requires more space than is available in the underlying `ArrayBuffer` will cuase an error. Below we attempt to write a `Float64` (which requires 8 bytes) at byte offset `0`, but there are only four total bytes in the buffer. + +```ts +dv.setFloat64(0, 3.1415); +// ^ RangeError: Out of bounds access +``` + +The following methods are available on `DataView`: + +{% table %} + +- Getters +- Setters + +--- + +- [`getBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigInt64) +- [`setBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigInt64) + +--- + +- [`getBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64) +- [`setBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigUint64) + +--- + +- [`getFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32) +- [`setFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32) + +--- + +- [`getFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64) +- [`setFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64) + +--- + +- [`getInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16) +- [`setInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16) + +--- + +- [`getInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32) +- [`setInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32) + +--- + +- [`getInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8) +- [`setInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8) + +--- + +- [`getUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16) +- [`setUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16) + +--- + +- [`getUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32) +- [`setUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32) + +--- + +- [`getUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8) +- [`setUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8) + +{% /table %} + +### `TypedArray` + +Typed arrays are a family of classes that provide an `Array`-like interface for interacting with data in an `ArrayBuffer`. Whereas a `DataView` lets you write numbers of varying size at a particular offset, a `TypedArray` interprets the underlying bytes as an array of numbers, each of a fixed size. + +{% callout %} +**Note** — It's common to refer to this family of classes collectively by their shared superclass `TypedArray`. This class as _internal_ to JavaScript; you can't directly create instances of it, and `TypedArray` is not defined in the global scope. Think of it as an `interface` or an abstract class. +{% /callout %} + +```ts +const buffer = new ArrayBuffer(3); +const arr = new Uint8Array(buffer); + +// contents are initialized to zero +console.log(arr); // Uint8Array(3) [0, 0, 0] + +// assign values like an array +arr[0] = 0; +arr[1] = 10; +arr[2] = 255; +arr[3] = 255; // no-op, out of bounds +``` + +While an `ArrayBuffer` is a generic sequence of bytes, these typed array classes interpret the bytes as an array of numbers of a given byte size. +The top row contains the raw bytes, and the later rows contain how these bytes will be interpreted when _viewed_ using different typed array classes. + +The following classes are typed arrays, along with a description of how they interpret the bytes in an `ArrayBuffer`: + +{% table %} + +- Class +- Description + +--- + +- [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) +- Every one (1) byte is interpreted as an unsigned 8-bit integer. Range 0 to 255. + +--- + +- [`Uint16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array) +- Every two (2) bytes are interpreted as an unsigned 16-bit integer. Range 0 to 65535. + +--- + +- [`Uint32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array) +- Every four (4) bytes are interpreted as an unsigned 32-bit integer. Range 0 to 4294967295. + +--- + +- [`Int8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array) +- Every one (1) byte is interpreted as a signed 8-bit integer. Range -128 to 127. + +--- + +- [`Int16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array) +- Every two (2) bytes are interpreted as a signed 16-bit integer. Range -32768 to 32767. + +--- + +- [`Int32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array) +- Every four (4) bytes are interpreted as a signed 32-bit integer. Range -2147483648 to 2147483647. + +--- + +- [`Float32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array) +- Every four (4) bytes are interpreted as a 32-bit floating point number. Range -3.4e38 to 3.4e38. + +--- + +- [`Float64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array) +- Every eight (8) bytes are interpreted as a 64-bit floating point number. Range -1.7e308 to 1.7e308. + +--- + +- [`BigInt64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array) +- Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range -9223372036854775808 to 9223372036854775807 (though `BigInt` is capable of representing larger numbers). + +--- + +- [`BigUint64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array) +- Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range 0 to 18446744073709551615 (though `BigInt` is capable of representing larger numbers). + +--- + +- [`Uint8ClampedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray) +- Same as `Uint8Array`, but automatically "clamps" to the range 0-255 when assigning a value to an element. + +{% /table %} + +The table below demonstrates how the bytes in an `ArrayBuffer` are interpreted when viewed using different typed array classes. + +{% table %} + +--- + +- `ArrayBuffer` +- `00000000` +- `00000001` +- `00000010` +- `00000011` +- `00000100` +- `00000101` +- `00000110` +- `00000111` + +--- + +- `Uint8Array` +- 0 +- 1 +- 2 +- 3 +- 4 +- 5 +- 6 +- 7 + +--- + +- `Uint16Array` +- 256 (`1 * 256 + 0`) {% colspan=2 %} +- 770 (`3 * 256 + 2`) {% colspan=2 %} +- 1284 (`5 * 256 + 4`) {% colspan=2 %} +- 1798 (`7 * 256 + 6`) {% colspan=2 %} + +--- + +- `Uint32Array` +- 50462976 {% colspan=4 %} +- 117835012 {% colspan=4 %} + +--- + +- `BigUint64Array` +- 506097522914230528n {% colspan=8 %} + +{% /table %} + +To create a typed array from a pre-defined `ArrayBuffer`: + +```ts +// create typed array from ArrayBuffer +const buf = new ArrayBuffer(10); +const arr = new Uint8Array(buf); + +arr[0] = 30; +arr[1] = 60; + +// all elements are initialized to zero +console.log(arr); // => Uint8Array(10) [ 30, 60, 0, 0, 0, 0, 0, 0, 0, 0 ]; +``` + +If we tried to instantiate a `Uint32Array` from this same `ArrayBuffer`, we'd get an error. + +```ts +const buf = new ArrayBuffer(10); +const arr = new Uint32Array(buf); +// ^ RangeError: ArrayBuffer length minus the byteOffset +// is not a multiple of the element size +``` + +A `Uint32` value requires four bytes (16 bits). Because the `ArrayBuffer` is 10 bytes long, there's no way to cleanly divide its contents into 4-byte chunks. + +To fix this, we can create a typed array over a particular "slice" of an `ArrayBuffer`. The `Uint16Array` below only "views" the _first_ 8 bytes of the underlying `ArrayBuffer`. To achieve these, we specify a `byteOffset` of `0` and a `length` of `2`, which indicates the number of `Uint32` numbers we want our array to hold. + +```ts +// create typed array from ArrayBuffer slice +const buf = new ArrayBuffer(10); +const arr = new Uint32Array(buf, 0, 2); + +/* + buf _ _ _ _ _ _ _ _ _ _ 10 bytes + arr [_______,_______] 2 4-byte elements +*/ + +arr.byteOffset; // 0 +arr.length; // 2 +``` + +You don't need to explicitly create an `ArrayBuffer` instance; you can instead directly specify a length in the typed array constructor: + +```ts +const arr2 = new Uint8Array(5); + +// all elements are initialized to zero +// => Uint8Array(5) [0, 0, 0, 0, 0] +``` + +Typed arrays can also be instantiated directly from an array of numbers, or another typed array: + +```ts +// from an array of numbers +const arr1 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); +arr1[0]; // => 0; +arr1[7]; // => 7; + +// from another typed array +const arr2 = new Uint8Array(arr); +``` + +Broadly speaking, typed arrays provide the same methods as regular arrays, with a few exceptions. For example, `push` and `pop` are not available on typed arrays, because they would require resizing the underlying `ArrayBuffer`. + +```ts +const arr = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + +// supports common array methods +arr.filter(n => n > 128); // Uint8Array(1) [255] +arr.map(n => n * 2); // Uint8Array(8) [0, 2, 4, 6, 8, 10, 12, 14] +arr.reduce((acc, n) => acc + n, 0); // 28 +arr.forEach(n => console.log(n)); // 0 1 2 3 4 5 6 7 +arr.every(n => n < 10); // true +arr.find(n => n > 5); // 6 +arr.includes(5); // true +arr.indexOf(5); // 5 +``` + +Refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) for more information on the properties and methods of typed arrays. + +### `Uint8Array` + +It's worth specifically highlighting `Uint8Array`, as it represents a classic "byte array"—a sequence of 8-bit unsigned integers between 0 and 255. This is the most common typed array you'll encounter in JavaScript. + +It is the return value of [`TextEncoder#encode`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder), and the input type of [`TextDecoder#decode`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder), two utility classes designed to translate strings and various binary encodings, most notably `"utf-8"`. + +```ts +const encoder = new TextEncoder(); +const bytes = encoder.encode("hello world"); +// => Uint8Array(11) [ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 ] + +const decoder = new TextDecoder(); +const text = decoder.decode(bytes); +// => hello world +``` + +### `Buffer` + +Bun implements `Buffer`, a Node.js API for working with binary data that pre-dates the introduction of typed arrays in the JavaScript spec. It has since been re-implemented as a subclass of `Uint8Array`. It provides a wide range of methods, including several Array-like and `DataView`-like methods. + +```ts +const buf = Buffer.from("hello world"); +// => Buffer(16) [ 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 116, 114, 105, 110, 103 ] + +buf.length; // => 11 +buf[0]; // => 104, ascii for 'h' +buf.writeUInt8(72, 0); // => ascii for 'H' + +console.log(buf.toString()); +// => Hello world +``` + +For complete documentation, refer to the [Node.js documentation](https://nodejs.org/api/buffer.html). + +## `Blob` + +`Blob` is a Web API commonly used for representing files. `Blob` was initially implemented in browsers (unlike `ArrayBuffer` which is part of JavaScript itself), but it is now supported in Node and Bun. + +It isn't common to directly create `Blob` instances. More often, you'll recieve instances of `Blob` from an external source (like an `<input type="file">` element in the browser) or library. That said, it is possible to create a `Blob` from one or more string or binary "blob parts". + +```ts +const blob = new Blob(["<html>Hello</html>"], { + type: "text/html", +}); + +blob.type; // => text/html +blob.size; // => 19 +``` + +These parts can be `string`, `ArrayBuffer`, `TypedArray`, `DataView`, or other `Blob` instances. The blob parts are concatenated together in the order they are provided. + +```ts +const blob = new Blob([ + "<html>", + new Blob(["<body>"]), + new Uint8Array([104, 101, 108, 108, 111]), // "hello" in binary + "</body></html>", +]); +``` + +The contents of a `Blob` can be asynchronously read in various formats. + +```ts +await blob.text(); // => <html><body>hello</body></html> +await blob.arrayBuffer(); // => ArrayBuffer (copies contents) +await blob.stream(); // => ReadableStream +``` + +### `BunFile` + +`BunFile` is a subclass of `Blob` used to represent a lazily-loaded file on disk. Like `File`, it adds a `name` and `lastModified` property. Unlike `File`, it does not require the file to be loaded into memory. + +```ts +const file = Bun.file("index.txt"); +// => BunFile +``` + +### `File` + +{% callout %} +Browser only. Experimental support in Node.js 20. +{% /callout %} + +[`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) is a subclass of `Blob` that adds a `name` and `lastModified` property. It's commonly used in the browser to represent files uploaded via a `<input type="file">` element. Node.js and Bun implement `File`. + +```ts +// on browser! +// <input type="file" id="file" /> + +const files = document.getElementById("file").files; +// => File[] +``` + +```ts +const file = new File(["<html>Hello</html>"], "index.html", { + type: "text/html", +}); +``` + +Refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Blob) for complete docs information. + +## Streams + +Streams are an important abstraction for working with binary data without loading it all into memory at once. They are commonly used for reading and writing files, sending and receiving network requests, and processing large amounts of data. + +Bun implements the Web APIs [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) and [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). + +{% callout %} +Bun also implements the `node:stream` module, including [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer to the Node.js docs. +{% /callout %} + +To create a simple readable stream: + +```ts +const stream = new ReadableStream({ + start(controller) { + controller.enqueue("hello"); + controller.enqueue("world"); + controller.close(); + }, +}); +``` + +The contents of this stream can be read chunk-by-chunk with `for await` syntax. + +```ts +for await (const chunk of stream) { + console.log(chunk); + // => "hello" + // => "world" +} +``` + +For a more complete discusson of streams in Bun, see [API > Streams](/docs/api/streams). + +## Conversion + +Converting from one binary format to another is a common task. This section is intended as a reference. + +### From `ArrayBuffer` + +Since `ArrayBuffer` stores the data that underlies other binary structures like `TypedArray`, the snippets below are not _converting_ from `ArrayBuffer` to another format. Instead, they are _creating_ a new instance using the data stored underlying data. + +#### To `TypedArray` + +```ts +new Uint8Array(buf); +``` + +#### To `DataView` + +```ts +new DataView(buf); +``` + +#### To `Buffer` + +```ts +// create Buffer over entire ArrayBuffer +Buffer.from(buf); + +// create Buffer over a slice of the ArrayBuffer +Buffer.from(buf, 0, 10); +``` + +#### To `string` + +```ts +new TextDecoder().decode(buf); +``` + +#### To `number[]` + +```ts +Array.from(new Uint8Array(buf)); +``` + +#### To `Blob` + +```ts +new Blob([buf], { type: "text/plain" }); +``` + +<!-- #### To `File` + +```ts +new File([buf], "filename.txt", { type: "text/plain", lastModified: Date.now() }); +``` --> + +#### To `ReadableStream` + +The following snippet creates a `ReadableStream` and enqueues the entire `ArrayBuffer` as a single chunk. + +```ts +new ReadableStream({ + start(controller) { + controller.enqueue(buf); + controller.close(); + }, +}); +``` + +{% details summary="With chunking" %} +To stream the `ArrayBuffer` in chunks, use a `Uint8Array` view and enqueue each chunk. + +```ts +const view = new Uint8Array(buf); +const chunkSize = 1024; + +new ReadableStream({ + start(controller) { + for (let i = 0; i < view.length; i += chunkSize) { + controller.enqueue(view.slice(i, i + chunkSize)); + } + controller.close(); + }, +}); +``` + +{% /details %} + +### From `TypedArray` + +#### To `ArrayBuffer` + +This retrieves the underlying `ArrayBuffer`. Note that a `TypedArray` can be a view of a _slice_ of the underlying buffer, so the sizes may differ. + +```ts +arr.buffer; +``` + +#### To `DataView` + +To creates a `DataView` over the same byte range as the TypedArray. + +```ts +new DataView(arr.buffer, arr.byteOffset, arr.byteLength); +``` + +#### To `Buffer` + +```ts +Buffer.from(arr); +``` + +#### To `string` + +```ts +new TextDecoder().decode(arr); +``` + +#### To `number[]` + +```ts +Array.from(arr); +``` + +#### To `Blob` + +```ts +new Blob([arr.buffer], { type: "text/plain" }); +``` + +<!-- #### To `File` + +```ts +new File([arr.buffer], "filename.txt", { type: "text/plain", lastModified: Date.now() }); +``` --> + +#### To `ReadableStream` + +```ts +new ReadableStream({ + start(controller) { + controller.enqueue(arr); + controller.close(); + }, +}); +``` + +{% details summary="With chunking" %} +To stream the `ArrayBuffer` in chunks, split the `TypedArray` into chunks and enqueue each one individually. + +```ts +new ReadableStream({ + start(controller) { + for (let i = 0; i < arr.length; i += chunkSize) { + controller.enqueue(arr.slice(i, i + chunkSize)); + } + controller.close(); + }, +}); +``` + +{% /details %} + +### From `DataView` + +#### To `ArrayBuffer` + +```ts +view.buffer; +``` + +#### To `TypedArray` + +Only works if the `byteLength` of the `DataView` is a multiple of the `BYTES_PER_ELEMENT` of the `TypedArray` subclass. + +```ts +new Uint8Array(view.buffer, view.byteOffset, view.byteLength); +new Uint16Array(view.buffer, view.byteOffset, view.byteLength / 2); +new Uint32Array(view.buffer, view.byteOffset, view.byteLength / 4); +// etc... +``` + +#### To `Buffer` + +```ts +Buffer.from(view.buffer, view.byteOffset, view.byteLength); +``` + +#### To `string` + +```ts +new TextDecoder().decode(view); +``` + +#### To `number[]` + +```ts +Array.from(view); +``` + +#### To `Blob` + +```ts +new Blob([view.buffer], { type: "text/plain" }); +``` + +<!-- #### To `File` + +```ts +new File([view.buffer], "filename.txt", { type: "text/plain", lastModified: Date.now() }); +``` --> + +#### To `ReadableStream` + +```ts +new ReadableStream({ + start(controller) { + controller.enqueue(view.buffer); + controller.close(); + }, +}); +``` + +{% details summary="With chunking" %} +To stream the `ArrayBuffer` in chunks, split the `DataView` into chunks and enqueue each one individually. + +```ts +new ReadableStream({ + start(controller) { + for (let i = 0; i < view.byteLength; i += chunkSize) { + controller.enqueue(view.buffer.slice(i, i + chunkSize)); + } + controller.close(); + }, +}); +``` + +{% /details %} + +### From `Buffer` + +#### To `ArrayBuffer` + +```ts +buf.buffer; +``` + +#### To `TypedArray` + +```ts +new Uint8Array(buf); +``` + +#### To `DataView` + +```ts +new DataView(buf.buffer, buf.byteOffset, buf.byteLength); +``` + +#### To `string` + +```ts +buf.toString(); +``` + +#### To `number[]` + +```ts +Array.from(buf); +``` + +#### To `Blob` + +```ts +new Blob([buf], { type: "text/plain" }); +``` + +<!-- #### To `File` + +```ts +new File([buf], "filename.txt", { type: "text/plain", lastModified: Date.now() }); +``` --> + +#### To `ReadableStream` + +```ts +new ReadableStream({ + start(controller) { + controller.enqueue(buf); + controller.close(); + }, +}); +``` + +{% details summary="With chunking" %} +To stream the `ArrayBuffer` in chunks, split the `Buffer` into chunks and enqueue each one individually. + +```ts +new ReadableStream({ + start(controller) { + for (let i = 0; i < buf.length; i += chunkSize) { + controller.enqueue(buf.slice(i, i + chunkSize)); + } + controller.close(); + }, +}); +``` + +{% /details %} + +### From `Blob` + +#### To `ArrayBuffer` + +The `Blob` class provides a convenience method for this purpose. + +```ts +await blob.arrayBuffer(); +``` + +#### To `TypedArray` + +```ts +new Uint8Array(await blob.arrayBuffer()); +``` + +#### To `DataView` + +```ts +new DataView(await blob.arrayBuffer()); +``` + +#### To `Buffer` + +```ts +Buffer.from(await blob.arrayBuffer()); +``` + +#### To `string` + +```ts +await blob.text(); +``` + +#### To `number[]` + +```ts +Array.from(new Uint8Array(await blob.arrayBuffer())); +``` + +#### To `ReadableStream` + +```ts +blob.stream(); +``` + +<!-- ### From `File` --> + +### From `ReadableStream` + +It's common to use [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) as a convenient intermediate representation to make it easier to convert `ReadableStream` to other formats. + +```ts +stream; // ReadableStream + +const buffer = new Response(stream).arrayBuffer(); +``` + +However this approach is verbose and adds overhead that slows down overall performance unnecessarily. Bun implements a set of optimized convenience functions for converting `ReadableStream` various binary formats. + +#### To `ArrayBuffer` + +```ts +// with Response +new Response(stream).arrayBuffer(); + +// with Bun function +Bun.readableStreamToArrayBuffer(stream); +``` + +#### To `TypedArray` + +```ts +// with Response +const buf = await new Response(stream).arrayBuffer(); +new Uint8Array(buf); + +// with Bun function +new Uint8Array(Bun.readableStreamToArrayBuffer(stream)); +``` + +#### To `DataView` + +```ts +// with Response +const buf = await new Response(stream).arrayBuffer(); +new DataView(buf); + +// with Bun function +new DataView(Bun.readableStreamToArrayBuffer(stream)); +``` + +#### To `Buffer` + +```ts +// with Response +const buf = await new Response(stream).arrayBuffer(); +Buffer.from(buf); + +// with Bun function +Buffer.from(Bun.readableStreamToArrayBuffer(stream)); +``` + +#### To `string` + +```ts +// with Response +new Response(stream).text(); + +// with Bun function +await Bun.readableStreamToString(stream); +``` + +#### To `number[]` + +```ts +// with Response +const buf = await new Response(stream).arrayBuffer(); +Array.from(new Uint8Array(buf)); + +// with Bun function +Array.from(new Uint8Array(Bun.readableStreamToArrayBuffer(stream))); +``` + +Bun provides a utility for resolving a `ReadableStream` to an array of its chunks. Each chunk may be a string, typed array, or `ArrayBuffer`. + +```ts +// with Bun function +Bun.readableStreamToArray(stream); +``` + +#### To `Blob` + +```ts +new Response(stream).blob(); +``` + +<!-- #### To `File` + +```ts +new Response(stream) + .blob() + .then(blob => new File([blob], "filename.txt", { type: "text/plain", lastModified: Date.now() })); +``` --> + +#### To `ReadableStream` + +To split a `ReadableStream` into two streams that can be consumed independently: + +```ts +const [a, b] = stream.tee(); +``` + +<!-- - Use Buffer +- TextEncoder +- `Bun.ArrayBufferSink` +- ReadableStream +- AsyncIterator +- TypedArray vs ArrayBuffer vs DataView +- Bun.indexOfLine +- “direct” readablestream + - readable stream has assumptions about + - its very generic + - all data is copies and queued + - direct : no queueing + - just a write function + - you can write strings + - more synchronous + - corking works better --> diff --git a/docs/api/hashing.md b/docs/api/hashing.md new file mode 100644 index 000000000..58bb05034 --- /dev/null +++ b/docs/api/hashing.md @@ -0,0 +1,139 @@ +{% callout %} + +Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in addition to the Bun-native APIs documented below. + +{% /callout %} + +## `Bun.hash` + +`Bun.hash` is a collection of utilities for _non-cryptographic_ hashing. Non-cryptographic hashing algorithms are optimized for speed of computation over collision-resistance or security. + +The standard `Bun.hash` functions uses [Wyhash](https://github.com/wangyi-fudan/wyhash) to generate a 64-bit hash from an input of arbitrary size. + +```ts +Bun.hash("some data here"); +// 976213160445840 +``` + +The input can be a string, `TypedArray`, `DataView`, `ArrayBuffer`, or `SharedArrayBuffer`. + +```ts +const arr = new Uint8Array([1, 2, 3, 4]); + +Bun.hash("some data here"); +Bun.hash(arr); +Bun.hash(arr.buffer); +Bun.hash(new DataView(arr.buffer)); +``` + +Optionally, an integer seed can be specified as the second parameter. + +```ts +Bun.hash("some data here", 1234); +// 1173484059023252 +``` + +Additional hashing algorithms are available as properties on `Bun.hash`. The API is the same for each. + +```ts +Bun.hash.wyhash("data", 1234); // equivalent to Bun.hash() +Bun.hash.crc32("data", 1234); +Bun.hash.adler32("data", 1234); +Bun.hash.cityHash32("data", 1234); +Bun.hash.cityHash64("data", 1234); +Bun.hash.murmur32v3("data", 1234); +Bun.hash.murmur64v2("data", 1234); +``` + +## `Bun.CryptoHasher` + +`Bun.CryptoHasher` is a general-purpose utility class that lets you incrementally compute a hash of string or binary data using a range of cryptographic hash algorithms. The following algorithms are supported: + +- `"blake2b256"` +- `"md4"` +- `"md5"` +- `"ripemd160"` +- `"sha1"` +- `"sha224"` +- `"sha256"` +- `"sha384"` +- `"sha512"` +- `"sha512-256"` + +```ts +const hasher = new Bun.CryptoHasher("sha256"); +hasher.update("hello world"); +hasher.digest(); +// Uint8Array(32) [ <byte>, <byte>, ... ] +``` + +Once initialized, data can be incrementally fed to to the hasher using `.update()`. This method accepts `string`, `TypedArray`, and `ArrayBuffer`. + +```ts +const hasher = new Bun.CryptoHasher(); + +hasher.update("hello world"); +hasher.update(new Uint8Array([1, 2, 3])); +hasher.update(new ArrayBuffer(10)); +``` + +If a `string` is passed, an optional second parameter can be used to specify the encoding (default `'utf-8'`). The following encodings are supported: + +{% table %} + +--- + +- Binary encodings +- `"base64"` `"base64url"` `"hex"` `"binary"` + +--- + +- Character encodings +- `"utf8"` `"utf-8"` `"utf16le"` `"latin1"` + +--- + +- Legacy character encodings +- `"ascii"` `"binary"` `"ucs2"` `"ucs-2"` + +{% /table %} + +```ts +hasher.update("hello world"); // defaults to utf8 +hasher.update("hello world", "hex"); +hasher.update("hello world", "base64"); +hasher.update("hello world", "latin1"); +``` + +After the data has been feed into the hasher, a final hash can be computed using `.digest()`. By default, this method returns a `Uint8Array` containing the hash. + +```ts +const hasher = new Bun.CryptoHasher(); +hasher.update("hello world"); + +hasher.digest(); +// => Uint8Array(32) [ 185, 77, 39, 185, 147, ... ] +``` + +The `.digest()` method can optionally return the hash as a string. To do so, specify an encoding: + +```ts +hasher.digest("base64"); +// => "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=" + +hasher.digest("hex"); +// => "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" +``` + +Alternatively, the method can write the hash into a pre-existing `TypedArray` instance. This may be desirable in some performance-sensitive applications. + +```ts +const arr = new Uint8Array(32); + +hasher.digest(arr); + +console.log(arr); +// => Uint8Array(32) [ 185, 77, 39, 185, 147, ... ] +``` + +<!-- Bun.sha; --> diff --git a/docs/api/http.md b/docs/api/http.md index 3ebdafab9..567560c3c 100644 --- a/docs/api/http.md +++ b/docs/api/http.md @@ -1,19 +1,12 @@ +The page primarily documents the Bun-native `Bun.serve` API. Bun also implements [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and the Node.js [`http`](https://nodejs.org/api/http.html) and [`https`](https://nodejs.org/api/https.html) modules. + {% callout %} -**Note** — This page documents the `Bun.serve` API. This API is heavily optimized and represents the recommended way to build HTTP servers in Bun. Existing Node.js projects may use Bun's [nearly complete](/docs/runtime/nodejs-apis#node_http) implementation of the Node.js [`http`](https://nodejs.org/api/http.html) and [`https`](https://nodejs.org/api/https.html) modules. +These modules have been re-implemented to use Bun's fast internal HTTP infrastructure. Feel free to use these modules directly; frameworks like [Express](https://expressjs.com/) that depend on these modules should work out of the box. For granular compatibility information, see [Runtime > Node.js APIs](/docs/runtime/nodejs-apis). {% /callout %} -## Send a request (`fetch()`) - -Bun implements the Web `fetch` API for making HTTP requests. The `fetch` function is available in the global scope. +To start a high-performance HTTP server with a clean API, the recommended approach is [`Bun.serve`](#start-a-server-bun-serve). -```ts -const response = await fetch("https://bun.sh/manifest.json"); -const result = (await response.json()) as any; -console.log(result.icons[0].src); -// => "/logo-square.jpg" -``` - -## Start a server (`Bun.serve()`) +## `Bun.serve()` Start an HTTP server in Bun with `Bun.serve`. @@ -50,7 +43,7 @@ Bun.serve({ }); ``` -### Error handling +## Error handling To activate development mode, set `development: true`. By default, development mode is _enabled_ unless `NODE_ENV` is `production`. @@ -100,36 +93,72 @@ const server = Bun.serve({ server.stop(); ``` -### TLS +## TLS -Bun supports TLS out of the box, powered by [OpenSSL](https://www.openssl.org/). Enable TLS by passing in a value for `keyFile` and `certFile`; both are required to enable TLS. If needed, supply a `passphrase` to decrypt the `keyFile`. +Bun supports TLS out of the box, powered by [OpenSSL](https://www.openssl.org/). Enable TLS by passing in a value for `key` and `cert`; both are required to enable TLS. If needed, supply a `passphrase` to decrypt the `keyFile`. ```ts Bun.serve({ fetch(req) { return new Response("Hello!!!"); }, - keyFile: "./key.pem", // path to TLS key - certFile: "./cert.pem", // path to TLS cert - passphrase: "super-secret", // optional passphrase + + // can be string, BunFile, TypedArray, Buffer, or array thereof + key: Bun.file("./key.pem"), + cert: Bun.file("./cert.pem"), + + // passphrase, only required if key is encrypted + passphrase: "super-secret", +}); +``` + +The `key` and `cert` fields expect the _contents_ of your TLS key and certificate. This can be a string, `BunFile`, `TypedArray`, or `Buffer`. + +```ts +Bun.serve({ + fetch() {}, + + // BunFile + key: Bun.file("./key.pem"), + // Buffer + key: fs.readFileSync("./key.pem"), + // string + key: fs.readFileSync("./key.pem", "utf8"), + // array of above + key: [Bun.file('./key1.pem'), Bun.file('./key2.pem'] + }); ``` -The root CA and Diffie-Helman parameters can be configured as well. +{% callout %} + +**Note** — Earlier versions of Bun supported passing a file path as `keyFile` and `certFile`; this has been deprecated as of `v0.6.3`. + +{% /callout %} + +Optionally, you can override the trusted CA certificates by passing a value for `ca`. By default, the server will trust the list of well-known CAs curated by Mozilla. When `ca` is specified, the Mozilla list is overwritten. ```ts Bun.serve({ fetch(req) { return new Response("Hello!!!"); }, - keyFile: "./key.pem", // path to TLS key - certFile: "./cert.pem", // path to TLS cert - caFile: "./ca.pem", // path to root CA certificate - dhParamsFile: "./dhparams.pem", // Diffie Helman parameters + key: Bun.file("./key.pem"), // path to TLS key + cert: Bun.file("./cert.pem"), // path to TLS cert + ca: Bun.file("./ca.pem"), // path to root CA certificate +}); +``` + +To override Diffie-Helman parameters: + +```ts +Bun.serve({ + // ... + dhParamsFile: "./dhparams.pem", // path to Diffie Helman parameters }); ``` -### Hot reloading +## Hot reloading Thus far, the examples on this page have used the explicit `Bun.serve` API. Bun also supports an alternate syntax. @@ -153,7 +182,7 @@ $ bun --hot server.ts It's possible to configure hot reloading while using the explicit `Bun.serve` API; for details refer to [Runtime > Hot reloading](/docs/runtime/hot). -### Streaming files +## Streaming files To stream a file, return a `Response` object with a `BunFile` object as the body. @@ -191,7 +220,7 @@ Bun.serve({ }); ``` -### Benchmarks +## Benchmarks Below are Bun and Node.js implementations of a simple HTTP server that responds `Bun!` to each incoming `Request`. @@ -234,7 +263,7 @@ The `Bun.serve` server can handle roughly 2.5x more requests per second than Nod {% image width="499" alt="image" src="https://user-images.githubusercontent.com/709451/162389032-fc302444-9d03-46be-ba87-c12bd8ce89a0.png" /%} -### Reference +## Reference {% details summary="See TypeScript definitions" %} diff --git a/docs/api/import-meta.md b/docs/api/import-meta.md new file mode 100644 index 000000000..8e148fffa --- /dev/null +++ b/docs/api/import-meta.md @@ -0,0 +1,52 @@ +The `import.meta` object is a way for a module to access information about itself. It's part of the JavaScript language, but its contents are not standardized. Each "host" (browser, runtime, etc) is free to implement any properties it wishes on the `import.meta` object. + +Bun implements the following properties. + +```ts#/path/to/project/file.ts +import.meta.dir; // => "/path/to/project" +import.meta.file; // => "file.ts" +import.meta.path; // => "/path/to/project/file.ts" + +import.meta.main; // `true` if this file is directly executed by `bun run` + // `false` otherwise + +import.meta.resolveSync("zod") +// resolve an import specifier relative to the directory +``` + +{% table %} + +--- + +- `import.meta.dir` +- Absolute path to the directory containing the current fil, e.g. `/path/to/project`. Equivalent to `__dirname` in Node.js. + +--- + +- `import.meta.file` +- The name of the current file, e.g. `index.tsx`. Equivalent to `__filename` in Node.js. + +--- + +- `import.meta.path` +- Absolute path to the current file, e.g. `/path/to/project/index.tx`. + +--- + +- `import.meta.main` +- `boolean` Indicates whether the current file is the entrypoint to the current `bun` process. Is the file being directly executed by `bun run` or is it being imported? + +--- + +- `import.meta.resolve{Sync}` +- Resolve a module specifier (e.g. `"zod"` or `"./file.tsx`) to an absolute path. While file would be imported if the specifier were imported from this file? + + ```ts + import.meta.resolveSync("zod"); + // => "/path/to/project/node_modules/zod/index.ts" + + import.meta.resolveSync("./file.tsx"); + // => "/path/to/project/file.tsx" + ``` + +{% /table %} diff --git a/docs/api/streams.md b/docs/api/streams.md new file mode 100644 index 000000000..7f3e3bcb4 --- /dev/null +++ b/docs/api/streams.md @@ -0,0 +1,172 @@ +Streams are an important abstraction for working with binary data without loading it all into memory at once. They are commonly used for reading and writing files, sending and receiving network requests, and processing large amounts of data. + +Bun implements the Web APIs [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) and [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). + +{% callout %} +Bun also implements the `node:stream` module, including [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer to the [Node.js docs](https://nodejs.org/api/stream.html). +{% /callout %} + +To create a simple `ReadableStream`: + +```ts +const stream = new ReadableStream({ + start(controller) { + controller.enqueue("hello"); + controller.enqueue("world"); + controller.close(); + }, +}); +``` + +The contents of a `ReadableStream` can be read chunk-by-chunk with `for await` syntax. + +```ts +for await (const chunk of stream) { + console.log(chunk); + // => "hello" + // => "world" +} +``` + +For a more complete discusson of streams in Bun, see [API > Streams](/docs/api/streams). + +## Direct `ReadableStream` + +Bun implements an optimized version of `ReadableStream` that avoid unnecessary data copying & queue management logic. With a traditional `ReadableStream`, chunks of data are _enqueued_. Each chunk is copied into a queue, where it sits until the stream is ready to send more data. + +```ts +const stream = new ReadableStream({ + start(controller) { + controller.enqueue("hello"); + controller.enqueue("world"); + controller.close(); + }, +}); +``` + +With a direct `ReadableStream`, chunks of data are written directly to the stream. No queueing happens, and there's no need to clone the chunk data into memory. The `controller` API is updated to reflect this; instead of `.enqueue()` you call `.write`. + +```ts +const stream = new ReadableStream({ + type: "direct", + pull(controller) { + controller.write("hello"); + controller.write("world"); + }, +}); +``` + +When using a direct `ReadableStream`, all chunk queueing is handled by the destination. The consumer of the stream receives exactly what is passed to `controller.write()`, without any encoding or modification. + +## `Bun.ArrayBufferSink` + +The `Bun.ArrayBufferSink` class is a fast incremental writer for constructing an `ArrayBuffer` of unknown size. + +```ts +const sink = new Bun.ArrayBufferSink(); + +sink.write("h"); +sink.write("e"); +sink.write("l"); +sink.write("l"); +sink.write("o"); + +sink.end(); +// ArrayBuffer(5) [ 104, 101, 108, 108, 111 ] +``` + +To instead retrieve the data as a `Uint8Array`, pass the `asUint8Array` option to the constructor. + +```ts-diff +const sink = new Bun.ArrayBufferSink({ ++ asUint8Array: true +}); + +sink.write("h"); +sink.write("e"); +sink.write("l"); +sink.write("l"); +sink.write("o"); + +sink.end(); +// Uint8Array(5) [ 104, 101, 108, 108, 111 ] +``` + +The `.write()` method supports strings, typed arrays, `ArrayBuffer`, and `SharedArrayBuffer`. + +```ts +sink.write("h"); +sink.write(new Uint8Array([101, 108])); +sink.write(Buffer.from("lo").buffer); + +sink.end(); +``` + +Once `.end()` is called, no more data can be written to the `ArrayBufferSink`. However, in the context of buffering a stream, it's useful to continuously write data and periodically `.flush()` the contents (say, into a `WriteableStream`). To support this, pass `stream: true` to the constructor. + +```ts +const sink = new Bun.ArrayBufferSink({ + stream: true, +}); + +sink.write("h"); +sink.write("e"); +sink.write("l"); +sink.flush(); +// ArrayBuffer(5) [ 104, 101, 108 ] + +sink.write("l"); +sink.write("o"); +sink.flush(); +// ArrayBuffer(5) [ 108, 111 ] +``` + +The `.flush()` method returns the buffered data as an `ArrayBuffer` (or `Uint8Array` if `asUint8Array: true`) and clears internal buffer. + +To manually set the size of the internal buffer in bytes, pass a value for `highWaterMark`: + +```ts +const sink = new Bun.ArrayBufferSink({ + highWaterMark: 1024 * 1024, // 1 MB +}); +``` + +{% details summary="Reference" %} + +```ts +/** + * Fast incremental writer that becomes an `ArrayBuffer` on end(). + */ +export class ArrayBufferSink { + constructor(); + + start(options?: { + asUint8Array?: boolean; + /** + * Preallocate an internal buffer of this size + * This can significantly improve performance when the chunk size is small + */ + highWaterMark?: number; + /** + * On {@link ArrayBufferSink.flush}, return the written data as a `Uint8Array`. + * Writes will restart from the beginning of the buffer. + */ + stream?: boolean; + }): void; + + write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number; + /** + * Flush the internal buffer + * + * If {@link ArrayBufferSink.start} was passed a `stream` option, this will return a `ArrayBuffer` + * If {@link ArrayBufferSink.start} was passed a `stream` option and `asUint8Array`, this will return a `Uint8Array` + * Otherwise, this will return the number of bytes written since the last flush + * + * This API might change later to separate Uint8ArraySink and ArrayBufferSink + */ + flush(): number | Uint8Array | ArrayBuffer; + end(): ArrayBuffer | Uint8Array; +} +``` + +{% /details %} diff --git a/docs/api/tcp.md b/docs/api/tcp.md index cb6d2d783..bcf86d9a8 100644 --- a/docs/api/tcp.md +++ b/docs/api/tcp.md @@ -59,7 +59,7 @@ Bun.listen<SocketData>({ }); ``` -To enable TLS, pass a `tls` object containing `keyFile` and `certFile` properties. +To enable TLS, pass a `tls` object containing `key` and `cert` fields. ```ts Bun.listen({ @@ -69,13 +69,38 @@ Bun.listen({ data(socket, data) {}, }, tls: { - certFile: "cert.pem", - keyFile: "key.pem", + // can be string, BunFile, TypedArray, Buffer, or array thereof + key: Bun.file("./key.pem"), + cert: Bun.file("./cert.pem"), }, }); ``` -The result of `Bun.listen` is a server that conforms to the `TCPSocket` instance. +{% callout %} + +**Note** — Earlier versions of Bun supported passing a file path as `keyFile` and `certFile`; this has been deprecated as of `v0.6.3`. + +{% /callout %} + +The `key` and `cert` fields expect the _contents_ of your TLS key and certificate. This can be a string, `BunFile`, `TypedArray`, or `Buffer`. + +```ts +Bun.listen({ + // ... + tls: { + // BunFile + key: Bun.file("./key.pem"), + // Buffer + key: fs.readFileSync("./key.pem"), + // string + key: fs.readFileSync("./key.pem", "utf8"), + // array of above + key: [Bun.file('./key1.pem'), Bun.file('./key2.pem'] + }, +}); +``` + +The result of `Bun.listen` is a server that conforms to the `TCPSocket` interface. ```ts const server = Bun.listen({ diff --git a/docs/api/utils.md b/docs/api/utils.md index 59167b2b7..7797650f5 100644 --- a/docs/api/utils.md +++ b/docs/api/utils.md @@ -1,4 +1,47 @@ -## `Bun.sleep` +## `Bun.version` + +A `string` containing the version of the `bun` CLI that is currently running. + +```ts +Bun.version; +// => "0.6.4" +``` + +## `Bun.revision` + +The git commit of [Bun](https://github.com/oven-sh/bun) that was compiled to create the current `bun` CLI. + +```ts +Bun.revision; +// => "f02561530fda1ee9396f51c8bc99b38716e38296" +``` + +## `Bun.env` + +An alias for `process.env`. + +## `Bun.main` + +An absolute path to the entrypoint of the current program (the file that was executed with `bun run`). + +```ts#script.ts +Bun.main; +// /path/to/script.ts +``` + +This is particular useful for determining whether a script is being directly executed, as opposed to being imported by another script. + +```ts +if (import.meta.path === Bun.main) { + // this script is being directly executed +} else { + // this file is being imported from another script +} +``` + +This is analogous to the [`require.main = module` trick](https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line) in Node.js. + +## `Bun.sleep()` `Bun.sleep(ms: number)` (added in Bun v0.5.6) @@ -20,11 +63,33 @@ await Bun.sleep(oneSecondInFuture); console.log("hello one second later!"); ``` -## `Bun.which` +## `Bun.sleepSync()` + +`Bun.sleepSync(ms: number)` (added in Bun v0.5.6) + +A blocking synchronous version of `Bun.sleep`. + +```ts +console.log("hello"); +Bun.sleepSync(1000); // blocks thread for one second +console.log("hello one second later!"); +``` + +Alternatively, pass a `Date` object to receive a `Promise` that resolves at that point in time. + +```ts +const oneSecondInFuture = new Date(Date.now() + 1000); + +console.log("hello"); +await Bun.sleep(oneSecondInFuture); +console.log("hello one second later!"); +``` + +## `Bun.which()` `Bun.which(bin: string)` -Find the path to an executable, similar to typing `which` in your terminal. +Returns the path to an executable, similar to typing `which` in your terminal. ```ts const ls = Bun.which("ls"); @@ -51,11 +116,11 @@ const ls = Bun.which("ls", { console.log(ls); // null ``` -## `Bun.peek` +## `Bun.peek()` `Bun.peek(prom: Promise)` (added in Bun v0.2.2) -`Bun.peek` is a utility function that lets you read a promise's result without `await` or `.then`, but only if the promise has already fulfilled or rejected. +Reads a promise's result without `await` or `.then`, but only if the promise has already fulfilled or rejected. ```ts import { peek } from "bun"; @@ -117,9 +182,9 @@ test("peek.status", () => { }); ``` -## `Bun.openInEditor` +## `Bun.openInEditor()` -Open a file in your default editor. Bun auto-detects your editor via the `$VISUAL` or `$EDITOR` environment variables. +Opens a file in your default editor. Bun auto-detects your editor via the `$VISUAL` or `$EDITOR` environment variables. ```ts const currentFile = import.meta.url; @@ -142,3 +207,290 @@ Bun.openInEditor(import.meta.url, { column: 5, }); ``` + +Bun.ArrayBufferSink; + +## `Bun.deepEquals()` + +Nestedly checks if two objects are equivalent. This is used internally by `expect().toEqual()` in `bun:test`. + +```ts +const foo = { a: 1, b: 2, c: { d: 3 } }; + +// true +Bun.deepEquals(foo, { a: 1, b: 2, c: { d: 3 } }); + +// false +Bun.deepEquals(foo, { a: 1, b: 2, c: { d: 4 } }); +``` + +A third boolean parameter can be used to enable "strict" mode. This is used by `expect().toStrictEqual()` in the test runner. + +```ts +const a = { entries: [1, 2] }; +const b = { entries: [1, 2], extra: undefined }; + +Bun.deepEquals(a, b); // => true +Bun.deepEquals(a, b, true); // => false +``` + +In strict mode, the following are considered unequal: + +```ts +// undefined values +Bun.deepEquals({}, { a: undefined }, true); // false + +// undefined in arrays +Bun.deepEquals(["asdf"], ["asdf", undefined], true); // false + +// sparse arrays +Bun.deepEquals([, 1], [undefined, 1], true); // false + +// object literals vs instances w/ same properties +class Foo { + a = 1; +} +Bun.deepEquals(new Foo(), { a: 1 }, true); // false +``` + +## `Bun.escapeHTML()` + +`Bun.escapeHTML(value: string | object | number | boolean): boolean` + +Escapes the following characters from an input string: + +- `"` becomes `"""` +- `&` becomes `"&"` +- `'` becomes `"'"` +- `<` becomes `"<"` +- `>` becomes `">"` + +This function is optimized for large input. On an M1X, it processes 480 MB/s - +20 GB/s, depending on how much data is being escaped and whether there is non-ascii +text. Non-string types will be converted to a string before escaping. + +<!-- ## `Bun.enableANSIColors()` --> + +## `Bun.fileURLToPath()` + +Converts a `file://` URL to an absolute path. + +```ts +const path = Bun.fileURLToPath(new URL("file:///foo/bar.txt")); +console.log(path); // "/foo/bar.txt" +``` + +## `Bun.pathToFileURL()` + +Converts an absolute path to a `file://` URL. + +```ts +const url = Bun.pathToFileURL("/foo/bar.txt"); +console.log(url); // "file:///foo/bar.txt" +``` + +<!-- Bun.hash; --> + +## `Bun.gzipSync()` + +Compresses a `Uint8Array` using zlib's DEFLATE algorithm. + +```ts +const buf = Buffer.from("hello".repeat(100)); // Buffer extends Uint8Array +const compressed = Bun.gzipSync(buf); + +buf; // => Uint8Array(500) +compressed; // => Uint8Array(30) +``` + +Optionally, pass a parameters object as the second argument: + +{% details summary="zlib compression options"%} + +```ts +export type ZlibCompressionOptions = { + /** + * The compression level to use. Must be between `-1` and `9`. + * - A value of `-1` uses the default compression level (Currently `6`) + * - A value of `0` gives no compression + * - A value of `1` gives least compression, fastest speed + * - A value of `9` gives best compression, slowest speed + */ + level?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; + /** + * How much memory should be allocated for the internal compression state. + * + * A value of `1` uses minimum memory but is slow and reduces compression ratio. + * + * A value of `9` uses maximum memory for optimal speed. The default is `8`. + */ + memLevel?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; + /** + * The base 2 logarithm of the window size (the size of the history buffer). + * + * Larger values of this parameter result in better compression at the expense of memory usage. + * + * The following value ranges are supported: + * - `9..15`: The output will have a zlib header and footer (Deflate) + * - `-9..-15`: The output will **not** have a zlib header or footer (Raw Deflate) + * - `25..31` (16+`9..15`): The output will have a gzip header and footer (gzip) + * + * The gzip header will have no file name, no extra data, no comment, no modification time (set to zero) and no header CRC. + */ + windowBits?: + | -9 + | -10 + | -11 + | -12 + | -13 + | -14 + | -15 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 25 + | 26 + | 27 + | 28 + | 29 + | 30 + | 31; + /** + * Tunes the compression algorithm. + * + * - `Z_DEFAULT_STRATEGY`: For normal data **(Default)** + * - `Z_FILTERED`: For data produced by a filter or predictor + * - `Z_HUFFMAN_ONLY`: Force Huffman encoding only (no string match) + * - `Z_RLE`: Limit match distances to one (run-length encoding) + * - `Z_FIXED` prevents the use of dynamic Huffman codes + * + * `Z_RLE` is designed to be almost as fast as `Z_HUFFMAN_ONLY`, but give better compression for PNG image data. + * + * `Z_FILTERED` forces more Huffman coding and less string matching, it is + * somewhat intermediate between `Z_DEFAULT_STRATEGY` and `Z_HUFFMAN_ONLY`. + * Filtered data consists mostly of small values with a somewhat random distribution. + */ + strategy?: number; +}; +``` + +{% /details %} + +## `Bun.gunzipSync()` + +Uncompresses a `Uint8Array` using zlib's INFLATE algorithm. + +```ts +const buf = Buffer.from("hello".repeat(100)); // Buffer extends Uint8Array +const compressed = Bun.gunzipSync(buf); + +const dec = new TextDecoder(); +const uncompressed = Bun.inflateSync(compressed); +dec.decode(uncompressed); +// => "hellohellohello..." +``` + +## `Bun.deflateSync()` + +Compresses a `Uint8Array` using zlib's DEFLATE algorithm. + +```ts +const buf = Buffer.from("hello".repeat(100)); +const compressed = Bun.deflateSync(buf); + +buf; // => Uint8Array(25) +compressed; // => Uint8Array(10) +``` + +The second argument supports the same set of configuration options as [`Bun.gzipSync`](#bun.gzipSync). + +## `Bun.inflateSync()` + +Uncompresses a `Uint8Array` using zlib's INFLATE algorithm. + +```ts +const buf = Buffer.from("hello".repeat(100)); +const compressed = Bun.deflateSync(buf); + +const dec = new TextDecoder(); +const uncompressed = Bun.inflateSync(compressed); +dec.decode(uncompressed); +// => "hellohellohello..." +``` + +## `Bun.inspect()` + +Serializes an object to a `string` exactly as it would be printed by `console.log`. + +```ts +const obj = { foo: "bar" }; +const str = Bun.inspect(obj); +// => '{\nfoo: "bar" \n}' + +const arr = new Uint8Array([1, 2, 3]); +const str = Bun.inspect(arr); +// => "Uint8Array(3) [ 1, 2, 3 ]" +``` + +## `Bun.nanoseconds()` + +Returns the number of nanoseconds since the current `bun` process started, as a `number`. Useful for high-precision timing and benchmarking. + +```ts +Bun.nanoseconds(); +// => 7288958 +``` + +## `Bun.readableStreamTo*()` + +Bun implements a set of convenience functions for asynchronously consuming the body of a `ReadableStream` and converting it to various binary formats. + +```ts +const stream = (await fetch("https://bun.sh")).body; +stream; // => ReadableStream + +await Bun.readableStreamToArrayBuffer(stream); +// => ArrayBuffer + +await Bun.readableStreamToBlob(stream); +// => Blob + +await Bun.readableStreamToJSON(stream); +// => object + +await Bun.readableStreamToText(stream); +// => string + +// returns all chunks as an array +await Bun.readableStreamToArray(stream); +// => unknown[] +``` + +## `Bun.resolveSync()` + +Resolves a file path or module specifier using Bun's internal module resolution algorithm. The first argument is the path to resolve, and the second argument is the "root". If no match is found, an `Error` is thrown. + +```ts +Bun.resolveSync("./foo.ts", "/path/to/project"); +// => "/path/to/project/foo.ts" + +Bun.resolveSync("zod", "/path/to/project"); +// => "/path/to/project/node_modules/zod/index.ts" +``` + +To resolve relative to the current working directory, pass `process.cwd` or `"."` as the root. + +```ts +Bun.resolveSync("./foo.ts", process.cwd()); +Bun.resolveSync("./foo.ts", "/path/to/project"); +``` + +To resolve relative to the directory containing the current file, pass `import.meta.dir`. + +```ts +Bun.resolveSync("./foo.ts", import.meta.dir); +``` |