bun-types 1.3.6-canary.20260108T140918 → 1.3.6-canary.20260110T140659

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bun.d.ts CHANGED
@@ -625,6 +625,33 @@ declare module "bun" {
625
625
  export function parse(input: string): object;
626
626
  }
627
627
 
628
+ /**
629
+ * JSONC related APIs
630
+ */
631
+ namespace JSONC {
632
+ /**
633
+ * Parse a JSONC (JSON with Comments) string into a JavaScript value.
634
+ *
635
+ * Supports both single-line (`//`) and block comments (`/* ... *\/`), as well as
636
+ * trailing commas in objects and arrays.
637
+ *
638
+ * @category Utilities
639
+ *
640
+ * @param input The JSONC string to parse
641
+ * @returns A JavaScript value
642
+ *
643
+ * @example
644
+ * ```js
645
+ * const result = Bun.JSONC.parse(`{
646
+ * // This is a comment
647
+ * "name": "my-app",
648
+ * "version": "1.0.0", // trailing comma is allowed
649
+ * }`);
650
+ * ```
651
+ */
652
+ export function parse(input: string): unknown;
653
+ }
654
+
628
655
  /**
629
656
  * YAML related APIs
630
657
  */
@@ -815,6 +842,20 @@ declare module "bun" {
815
842
  destination: BunFile,
816
843
  input: BunFile,
817
844
  options?: {
845
+ /**
846
+ * Set the file permissions of the destination when it is created or overwritten.
847
+ *
848
+ * Must be a valid Unix permission mode (0 to 0o777 / 511 in decimal).
849
+ * If omitted, defaults to the system default based on umask (typically 0o644).
850
+ *
851
+ * @throws {RangeError} If the mode is outside the valid range (0 to 0o777).
852
+ *
853
+ * @example
854
+ * ```ts
855
+ * await Bun.write(Bun.file("./secret.txt"), Bun.file("./source.txt"), { mode: 0o600 });
856
+ * ```
857
+ */
858
+ mode?: number;
818
859
  /**
819
860
  * If `true`, create the parent directory if it doesn't exist. By default, this is `true`.
820
861
  *
@@ -848,6 +889,20 @@ declare module "bun" {
848
889
  destinationPath: PathLike,
849
890
  input: BunFile,
850
891
  options?: {
892
+ /**
893
+ * Set the file permissions of the destination when it is created or overwritten.
894
+ *
895
+ * Must be a valid Unix permission mode (0 to 0o777 / 511 in decimal).
896
+ * If omitted, defaults to the system default based on umask (typically 0o644).
897
+ *
898
+ * @throws {RangeError} If the mode is outside the valid range (0 to 0o777).
899
+ *
900
+ * @example
901
+ * ```ts
902
+ * await Bun.write("./secret.txt", Bun.file("./source.txt"), { mode: 0o600 });
903
+ * ```
904
+ */
905
+ mode?: number;
851
906
  /**
852
907
  * If `true`, create the parent directory if it doesn't exist. By default, this is `true`.
853
908
  *
@@ -1952,6 +2007,65 @@ declare module "bun" {
1952
2007
  */
1953
2008
  reactFastRefresh?: boolean;
1954
2009
 
2010
+ /**
2011
+ * A map of file paths to their contents for in-memory bundling.
2012
+ *
2013
+ * This allows you to bundle virtual files that don't exist on disk, or override
2014
+ * the contents of files that do exist on disk. The keys are file paths (which should
2015
+ * match how they're imported) and the values are the file contents.
2016
+ *
2017
+ * File contents can be provided as:
2018
+ * - `string` - The source code as a string
2019
+ * - `Blob` - A Blob containing the source code
2020
+ * - `NodeJS.TypedArray` - A typed array (e.g., `Uint8Array`) containing the source code
2021
+ * - `ArrayBufferLike` - An ArrayBuffer containing the source code
2022
+ *
2023
+ * @example
2024
+ * ```ts
2025
+ * // Bundle entirely from memory (no files on disk needed)
2026
+ * await Bun.build({
2027
+ * entrypoints: ["/app/index.ts"],
2028
+ * files: {
2029
+ * "/app/index.ts": `
2030
+ * import { helper } from "./helper.ts";
2031
+ * console.log(helper());
2032
+ * `,
2033
+ * "/app/helper.ts": `
2034
+ * export function helper() {
2035
+ * return "Hello from memory!";
2036
+ * }
2037
+ * `,
2038
+ * },
2039
+ * });
2040
+ * ```
2041
+ *
2042
+ * @example
2043
+ * ```ts
2044
+ * // Override a file on disk with in-memory contents
2045
+ * await Bun.build({
2046
+ * entrypoints: ["./src/index.ts"],
2047
+ * files: {
2048
+ * // This will be used instead of the actual ./src/config.ts file
2049
+ * "./src/config.ts": `export const API_URL = "https://production.api.com";`,
2050
+ * },
2051
+ * });
2052
+ * ```
2053
+ *
2054
+ * @example
2055
+ * ```ts
2056
+ * // Mix disk files with in-memory files
2057
+ * // Entry point is on disk, but imports a virtual file
2058
+ * await Bun.build({
2059
+ * entrypoints: ["./src/index.ts"], // Real file on disk
2060
+ * files: {
2061
+ * // Virtual file that ./src/index.ts can import via "./generated.ts"
2062
+ * "./src/generated.ts": `export const BUILD_TIME = ${Date.now()};`,
2063
+ * },
2064
+ * });
2065
+ * ```
2066
+ */
2067
+ files?: Record<string, string | Blob | NodeJS.TypedArray | ArrayBufferLike>;
2068
+
1955
2069
  /**
1956
2070
  * Generate a JSON file containing metadata about the build.
1957
2071
  *
@@ -3204,16 +3318,29 @@ declare module "bun" {
3204
3318
 
3205
3319
  type WebSocketOptionsTLS = {
3206
3320
  /**
3207
- * Options for the TLS connection
3321
+ * Options for the TLS connection.
3322
+ *
3323
+ * Supports full TLS configuration including custom CA certificates,
3324
+ * client certificates, and other TLS settings (same as fetch).
3325
+ *
3326
+ * @example
3327
+ * ```ts
3328
+ * // Using BunFile for certificates
3329
+ * const ws = new WebSocket("wss://example.com", {
3330
+ * tls: {
3331
+ * ca: Bun.file("./ca.pem")
3332
+ * }
3333
+ * });
3334
+ *
3335
+ * // Using Buffer
3336
+ * const ws = new WebSocket("wss://example.com", {
3337
+ * tls: {
3338
+ * ca: fs.readFileSync("./ca.pem")
3339
+ * }
3340
+ * });
3341
+ * ```
3208
3342
  */
3209
- tls?: {
3210
- /**
3211
- * Whether to reject the connection if the certificate is not valid
3212
- *
3213
- * @default true
3214
- */
3215
- rejectUnauthorized?: boolean;
3216
- };
3343
+ tls?: TLSOptions;
3217
3344
  };
3218
3345
 
3219
3346
  type WebSocketOptionsHeaders = {
@@ -3223,10 +3350,57 @@ declare module "bun" {
3223
3350
  headers?: import("node:http").OutgoingHttpHeaders;
3224
3351
  };
3225
3352
 
3353
+ type WebSocketOptionsProxy = {
3354
+ /**
3355
+ * HTTP proxy to use for the WebSocket connection.
3356
+ *
3357
+ * Can be a string URL or an object with `url` and optional `headers`.
3358
+ *
3359
+ * @example
3360
+ * ```ts
3361
+ * // String format
3362
+ * const ws = new WebSocket("wss://example.com", {
3363
+ * proxy: "http://proxy.example.com:8080"
3364
+ * });
3365
+ *
3366
+ * // With credentials
3367
+ * const ws = new WebSocket("wss://example.com", {
3368
+ * proxy: "http://user:pass@proxy.example.com:8080"
3369
+ * });
3370
+ *
3371
+ * // Object format with custom headers
3372
+ * const ws = new WebSocket("wss://example.com", {
3373
+ * proxy: {
3374
+ * url: "http://proxy.example.com:8080",
3375
+ * headers: {
3376
+ * "Proxy-Authorization": "Bearer token"
3377
+ * }
3378
+ * }
3379
+ * });
3380
+ * ```
3381
+ */
3382
+ proxy?:
3383
+ | string
3384
+ | {
3385
+ /**
3386
+ * The proxy URL (http:// or https://)
3387
+ */
3388
+ url: string;
3389
+ /**
3390
+ * Custom headers to send to the proxy server.
3391
+ * Supports plain objects or Headers class instances.
3392
+ */
3393
+ headers?: import("node:http").OutgoingHttpHeaders | Headers;
3394
+ };
3395
+ };
3396
+
3226
3397
  /**
3227
3398
  * Constructor options for the `Bun.WebSocket` client
3228
3399
  */
3229
- type WebSocketOptions = WebSocketOptionsProtocolsOrProtocol & WebSocketOptionsTLS & WebSocketOptionsHeaders;
3400
+ type WebSocketOptions = WebSocketOptionsProtocolsOrProtocol &
3401
+ WebSocketOptionsTLS &
3402
+ WebSocketOptionsHeaders &
3403
+ WebSocketOptionsProxy;
3230
3404
 
3231
3405
  interface WebSocketEventMap {
3232
3406
  close: CloseEvent;
@@ -6791,6 +6965,296 @@ declare module "bun" {
6791
6965
  match(str: string): boolean;
6792
6966
  }
6793
6967
 
6968
+ /**
6969
+ * Input data for creating an archive. Can be:
6970
+ * - An object mapping paths to file contents (string, Blob, TypedArray, or ArrayBuffer)
6971
+ * - A Blob containing existing archive data
6972
+ * - A TypedArray or ArrayBuffer containing existing archive data
6973
+ */
6974
+ type ArchiveInput = Record<string, BlobPart> | Blob | ArrayBufferView | ArrayBufferLike;
6975
+
6976
+ /**
6977
+ * Compression format for archive output.
6978
+ * - `"gzip"` - Compress with gzip
6979
+ * - `true` - Same as `"gzip"`
6980
+ * - `false` - Explicitly disable compression (no compression)
6981
+ * - `undefined` - No compression (default behavior when omitted)
6982
+ *
6983
+ * Both `false` and `undefined` result in no compression; `false` can be used
6984
+ * to explicitly indicate "no compression" in code where the intent should be clear.
6985
+ */
6986
+ type ArchiveCompression = "gzip" | boolean;
6987
+
6988
+ /**
6989
+ * Options for extracting archive contents.
6990
+ */
6991
+ interface ArchiveExtractOptions {
6992
+ /**
6993
+ * Glob pattern(s) to filter which entries are extracted.
6994
+ * Uses the same syntax as {@link Bun.Glob}, including support for wildcards (`*`, `**`),
6995
+ * character classes (`[abc]`), alternation (`{a,b}`), and negation (`!pattern`).
6996
+ *
6997
+ * Patterns are matched against archive entry paths normalized to use forward slashes (`/`),
6998
+ * regardless of the host operating system. Always write patterns using `/` as the separator.
6999
+ *
7000
+ * - Positive patterns: Only entries matching at least one pattern will be extracted.
7001
+ * - Negative patterns (prefixed with `!`): Entries matching these patterns will be excluded.
7002
+ * Negative patterns are applied after positive patterns.
7003
+ *
7004
+ * If not specified, all entries are extracted.
7005
+ *
7006
+ * @example
7007
+ * ```ts
7008
+ * // Extract only TypeScript files
7009
+ * await archive.extract("./out", { glob: "**" + "/*.ts" });
7010
+ *
7011
+ * // Extract files from multiple directories
7012
+ * await archive.extract("./out", { glob: ["src/**", "lib/**"] });
7013
+ *
7014
+ * // Exclude node_modules using negative pattern
7015
+ * await archive.extract("./out", { glob: ["**", "!node_modules/**"] });
7016
+ *
7017
+ * // Extract source files but exclude tests
7018
+ * await archive.extract("./out", { glob: ["src/**", "!**" + "/*.test.ts"] });
7019
+ * ```
7020
+ */
7021
+ glob?: string | readonly string[];
7022
+ }
7023
+
7024
+ /**
7025
+ * A class for creating and extracting tar archives with optional gzip compression.
7026
+ *
7027
+ * `Bun.Archive` provides a fast, native implementation for working with tar archives.
7028
+ * It supports creating archives from in-memory data or extracting existing archives
7029
+ * to disk or memory.
7030
+ *
7031
+ * @example
7032
+ * **Create an archive from an object:**
7033
+ * ```ts
7034
+ * const archive = Bun.Archive.from({
7035
+ * "hello.txt": "Hello, World!",
7036
+ * "data.json": JSON.stringify({ foo: "bar" }),
7037
+ * "binary.bin": new Uint8Array([1, 2, 3, 4]),
7038
+ * });
7039
+ * ```
7040
+ *
7041
+ * @example
7042
+ * **Extract an archive to disk:**
7043
+ * ```ts
7044
+ * const archive = Bun.Archive.from(tarballBytes);
7045
+ * const entryCount = await archive.extract("./output");
7046
+ * console.log(`Extracted ${entryCount} entries`);
7047
+ * ```
7048
+ *
7049
+ * @example
7050
+ * **Get archive contents as a Map of File objects:**
7051
+ * ```ts
7052
+ * const archive = Bun.Archive.from(tarballBytes);
7053
+ * const entries = await archive.files();
7054
+ * for (const [path, file] of entries) {
7055
+ * console.log(path, await file.text());
7056
+ * }
7057
+ * ```
7058
+ *
7059
+ * @example
7060
+ * **Write a gzipped archive directly to disk:**
7061
+ * ```ts
7062
+ * await Bun.Archive.write("bundle.tar.gz", {
7063
+ * "src/index.ts": sourceCode,
7064
+ * "package.json": packageJson,
7065
+ * }, "gzip");
7066
+ * ```
7067
+ */
7068
+ export class Archive {
7069
+ /**
7070
+ * Create an `Archive` instance from input data.
7071
+ *
7072
+ * @param data - The input data for the archive:
7073
+ * - **Object**: Creates a new tarball with the object's keys as file paths and values as file contents
7074
+ * - **Blob/TypedArray/ArrayBuffer**: Wraps existing archive data (tar or tar.gz)
7075
+ *
7076
+ * @returns A new `Archive` instance
7077
+ *
7078
+ * @example
7079
+ * **From an object (creates new tarball):**
7080
+ * ```ts
7081
+ * const archive = Bun.Archive.from({
7082
+ * "hello.txt": "Hello, World!",
7083
+ * "nested/file.txt": "Nested content",
7084
+ * });
7085
+ * ```
7086
+ *
7087
+ * @example
7088
+ * **From existing archive data:**
7089
+ * ```ts
7090
+ * const response = await fetch("https://example.com/package.tar.gz");
7091
+ * const archive = Bun.Archive.from(await response.blob());
7092
+ * ```
7093
+ */
7094
+ static from(data: ArchiveInput): Archive;
7095
+
7096
+ /**
7097
+ * Create and write an archive directly to disk in one operation.
7098
+ *
7099
+ * This is more efficient than creating an archive and then writing it separately,
7100
+ * as it streams the data directly to disk.
7101
+ *
7102
+ * @param path - The file path to write the archive to
7103
+ * @param data - The input data for the archive (same as `Archive.from()`)
7104
+ * @param compress - Optional compression: `"gzip"`, `true` for gzip, or `false`/`undefined` for none
7105
+ *
7106
+ * @returns A promise that resolves when the write is complete
7107
+ *
7108
+ * @example
7109
+ * **Write uncompressed tarball:**
7110
+ * ```ts
7111
+ * await Bun.Archive.write("output.tar", {
7112
+ * "file1.txt": "content1",
7113
+ * "file2.txt": "content2",
7114
+ * });
7115
+ * ```
7116
+ *
7117
+ * @example
7118
+ * **Write gzipped tarball:**
7119
+ * ```ts
7120
+ * await Bun.Archive.write("output.tar.gz", files, "gzip");
7121
+ * ```
7122
+ */
7123
+ static write(path: string, data: ArchiveInput | Archive, compress?: ArchiveCompression): Promise<void>;
7124
+
7125
+ /**
7126
+ * Extract the archive contents to a directory on disk.
7127
+ *
7128
+ * Creates the target directory and any necessary parent directories if they don't exist.
7129
+ * Existing files will be overwritten.
7130
+ *
7131
+ * @param path - The directory path to extract to
7132
+ * @param options - Optional extraction options
7133
+ * @param options.glob - Glob pattern(s) to filter entries (positive patterns include, negative patterns starting with `!` exclude)
7134
+ * @returns A promise that resolves with the number of entries extracted (files, directories, and symlinks)
7135
+ *
7136
+ * @example
7137
+ * **Extract all entries:**
7138
+ * ```ts
7139
+ * const archive = Bun.Archive.from(tarballBytes);
7140
+ * const count = await archive.extract("./extracted");
7141
+ * console.log(`Extracted ${count} entries`);
7142
+ * ```
7143
+ *
7144
+ * @example
7145
+ * **Extract only TypeScript files:**
7146
+ * ```ts
7147
+ * const count = await archive.extract("./src", { glob: "**" + "/*.ts" });
7148
+ * ```
7149
+ *
7150
+ * @example
7151
+ * **Extract everything except tests:**
7152
+ * ```ts
7153
+ * const count = await archive.extract("./dist", { glob: ["**", "!**" + "/*.test.*"] });
7154
+ * ```
7155
+ *
7156
+ * @example
7157
+ * **Extract source files but exclude tests:**
7158
+ * ```ts
7159
+ * const count = await archive.extract("./output", {
7160
+ * glob: ["src/**", "lib/**", "!**" + "/*.test.ts", "!**" + "/__tests__/**"]
7161
+ * });
7162
+ * ```
7163
+ */
7164
+ extract(path: string, options?: ArchiveExtractOptions): Promise<number>;
7165
+
7166
+ /**
7167
+ * Get the archive contents as a `Blob`.
7168
+ *
7169
+ * @param compress - Optional compression: `"gzip"`, `true` for gzip, or `false`/`undefined` for none
7170
+ * @returns A promise that resolves with the archive data as a Blob
7171
+ *
7172
+ * @example
7173
+ * **Get uncompressed tarball:**
7174
+ * ```ts
7175
+ * const blob = await archive.blob();
7176
+ * ```
7177
+ *
7178
+ * @example
7179
+ * **Get gzipped tarball:**
7180
+ * ```ts
7181
+ * const gzippedBlob = await archive.blob("gzip");
7182
+ * ```
7183
+ */
7184
+ blob(compress?: ArchiveCompression): Promise<Blob>;
7185
+
7186
+ /**
7187
+ * Get the archive contents as a `Uint8Array`.
7188
+ *
7189
+ * @param compress - Optional compression: `"gzip"`, `true` for gzip, or `false`/`undefined` for none
7190
+ * @returns A promise that resolves with the archive data as a Uint8Array
7191
+ *
7192
+ * @example
7193
+ * **Get uncompressed tarball bytes:**
7194
+ * ```ts
7195
+ * const bytes = await archive.bytes();
7196
+ * ```
7197
+ *
7198
+ * @example
7199
+ * **Get gzipped tarball bytes:**
7200
+ * ```ts
7201
+ * const gzippedBytes = await archive.bytes("gzip");
7202
+ * ```
7203
+ */
7204
+ bytes(compress?: ArchiveCompression): Promise<Uint8Array<ArrayBuffer>>;
7205
+
7206
+ /**
7207
+ * Get the archive contents as a `Map` of `File` objects.
7208
+ *
7209
+ * Each file in the archive is returned as a `File` object with:
7210
+ * - `name`: The file path within the archive
7211
+ * - `lastModified`: The file's modification time from the archive
7212
+ * - Standard Blob methods (`text()`, `arrayBuffer()`, `stream()`, etc.)
7213
+ *
7214
+ * Only regular files are included; directories are not returned.
7215
+ * File contents are loaded into memory, so for large archives consider using `extract()` instead.
7216
+ *
7217
+ * @param glob - Optional glob pattern(s) to filter files. Supports the same syntax as {@link Bun.Glob},
7218
+ * including negation patterns (prefixed with `!`). Patterns are matched against paths normalized
7219
+ * to use forward slashes (`/`).
7220
+ * @returns A promise that resolves with a Map where keys are file paths (always using forward slashes `/` as separators) and values are File objects
7221
+ *
7222
+ * @example
7223
+ * **Get all files:**
7224
+ * ```ts
7225
+ * const entries = await archive.files();
7226
+ * for (const [path, file] of entries) {
7227
+ * console.log(`${path}: ${file.size} bytes`);
7228
+ * }
7229
+ * ```
7230
+ *
7231
+ * @example
7232
+ * **Filter by glob pattern:**
7233
+ * ```ts
7234
+ * const tsFiles = await archive.files("**" + "/*.ts");
7235
+ * const srcFiles = await archive.files(["src/**", "lib/**"]);
7236
+ * ```
7237
+ *
7238
+ * @example
7239
+ * **Exclude files with negative patterns:**
7240
+ * ```ts
7241
+ * // Get all source files except tests
7242
+ * const srcFiles = await archive.files(["src/**", "!**" + "/*.test.ts"]);
7243
+ * ```
7244
+ *
7245
+ * @example
7246
+ * **Read file contents:**
7247
+ * ```ts
7248
+ * const entries = await archive.files();
7249
+ * const readme = entries.get("README.md");
7250
+ * if (readme) {
7251
+ * console.log(await readme.text());
7252
+ * }
7253
+ * ```
7254
+ */
7255
+ files(glob?: string | readonly string[]): Promise<Map<string, File>>;
7256
+ }
7257
+
6794
7258
  /**
6795
7259
  * Generate a UUIDv7, which is a sequential ID based on the current timestamp with a random component.
6796
7260
  *
@@ -220,6 +220,78 @@ An array of paths corresponding to the entrypoints of our application. One bundl
220
220
  </Tab>
221
221
  </Tabs>
222
222
 
223
+ ### files
224
+
225
+ A map of file paths to their contents for in-memory bundling. This allows you to bundle virtual files that don't exist on disk, or override the contents of files that do exist. This option is only available in the JavaScript API.
226
+
227
+ File contents can be provided as a `string`, `Blob`, `TypedArray`, or `ArrayBuffer`.
228
+
229
+ #### Bundle entirely from memory
230
+
231
+ You can bundle code without any files on disk by providing all sources via `files`:
232
+
233
+ ```ts title="build.ts" icon="/icons/typescript.svg"
234
+ const result = await Bun.build({
235
+ entrypoints: ["/app/index.ts"],
236
+ files: {
237
+ "/app/index.ts": `
238
+ import { greet } from "./greet.ts";
239
+ console.log(greet("World"));
240
+ `,
241
+ "/app/greet.ts": `
242
+ export function greet(name: string) {
243
+ return "Hello, " + name + "!";
244
+ }
245
+ `,
246
+ },
247
+ });
248
+
249
+ const output = await result.outputs[0].text();
250
+ console.log(output);
251
+ ```
252
+
253
+ When all entrypoints are in the `files` map, the current working directory is used as the root.
254
+
255
+ #### Override files on disk
256
+
257
+ In-memory files take priority over files on disk. This lets you override specific files while keeping the rest of your codebase unchanged:
258
+
259
+ ```ts title="build.ts" icon="/icons/typescript.svg"
260
+ // Assume ./src/config.ts exists on disk with development settings
261
+ await Bun.build({
262
+ entrypoints: ["./src/index.ts"],
263
+ files: {
264
+ // Override config.ts with production values
265
+ "./src/config.ts": `
266
+ export const API_URL = "https://api.production.com";
267
+ export const DEBUG = false;
268
+ `,
269
+ },
270
+ outdir: "./dist",
271
+ });
272
+ ```
273
+
274
+ #### Mix disk and virtual files
275
+
276
+ Real files on disk can import virtual files, and virtual files can import real files:
277
+
278
+ ```ts title="build.ts" icon="/icons/typescript.svg"
279
+ // ./src/index.ts exists on disk and imports "./generated.ts"
280
+ await Bun.build({
281
+ entrypoints: ["./src/index.ts"],
282
+ files: {
283
+ // Provide a virtual file that index.ts imports
284
+ "./src/generated.ts": `
285
+ export const BUILD_ID = "${crypto.randomUUID()}";
286
+ export const BUILD_TIME = ${Date.now()};
287
+ `,
288
+ },
289
+ outdir: "./dist",
290
+ });
291
+ ```
292
+
293
+ This is useful for code generation, injecting build-time constants, or testing with mock modules.
294
+
223
295
  ### outdir
224
296
 
225
297
  The directory where output files will be written.
@@ -0,0 +1,444 @@
1
+ ---
2
+ title: Archive
3
+ description: Create and extract tar archives with Bun's fast native implementation
4
+ ---
5
+
6
+ Bun provides a fast, native implementation for working with tar archives through `Bun.Archive`. It supports creating archives from in-memory data, extracting archives to disk, and reading archive contents without extraction.
7
+
8
+ ## Quickstart
9
+
10
+ **Create an archive from files:**
11
+
12
+ ```ts
13
+ const archive = Bun.Archive.from({
14
+ "hello.txt": "Hello, World!",
15
+ "data.json": JSON.stringify({ foo: "bar" }),
16
+ "nested/file.txt": "Nested content",
17
+ });
18
+
19
+ // Write to disk
20
+ await Bun.Archive.write("bundle.tar", archive);
21
+ ```
22
+
23
+ **Extract an archive:**
24
+
25
+ ```ts
26
+ const tarball = await Bun.file("package.tar.gz").bytes();
27
+ const archive = Bun.Archive.from(tarball);
28
+ const entryCount = await archive.extract("./output");
29
+ console.log(`Extracted ${entryCount} entries`);
30
+ ```
31
+
32
+ **Read archive contents without extracting:**
33
+
34
+ ```ts
35
+ const tarball = await Bun.file("package.tar.gz").bytes();
36
+ const archive = Bun.Archive.from(tarball);
37
+ const files = await archive.files();
38
+
39
+ for (const [path, file] of files) {
40
+ console.log(`${path}: ${await file.text()}`);
41
+ }
42
+ ```
43
+
44
+ ## Creating Archives
45
+
46
+ Use `Bun.Archive.from()` to create an archive from an object where keys are file paths and values are file contents:
47
+
48
+ ```ts
49
+ const archive = Bun.Archive.from({
50
+ "README.md": "# My Project",
51
+ "src/index.ts": "console.log('Hello');",
52
+ "package.json": JSON.stringify({ name: "my-project" }),
53
+ });
54
+ ```
55
+
56
+ File contents can be:
57
+
58
+ - **Strings** - Text content
59
+ - **Blobs** - Binary data
60
+ - **ArrayBufferViews** (e.g., `Uint8Array`) - Raw bytes
61
+ - **ArrayBuffers** - Raw binary data
62
+
63
+ ```ts
64
+ const data = "binary data";
65
+ const arrayBuffer = new ArrayBuffer(8);
66
+
67
+ const archive = Bun.Archive.from({
68
+ "text.txt": "Plain text",
69
+ "blob.bin": new Blob([data]),
70
+ "bytes.bin": new Uint8Array([1, 2, 3, 4]),
71
+ "buffer.bin": arrayBuffer,
72
+ });
73
+ ```
74
+
75
+ ### Writing Archives to Disk
76
+
77
+ Use `Bun.Archive.write()` to create and write an archive in one operation:
78
+
79
+ ```ts
80
+ // Write uncompressed tar
81
+ await Bun.Archive.write("output.tar", {
82
+ "file1.txt": "content1",
83
+ "file2.txt": "content2",
84
+ });
85
+
86
+ // Write gzipped tar
87
+ const files = { "src/index.ts": "console.log('Hello');" };
88
+ await Bun.Archive.write("output.tar.gz", files, "gzip");
89
+ ```
90
+
91
+ ### Getting Archive Bytes
92
+
93
+ Get the archive data as bytes or a Blob:
94
+
95
+ ```ts
96
+ const files = { "hello.txt": "Hello, World!" };
97
+ const archive = Bun.Archive.from(files);
98
+
99
+ // As Uint8Array
100
+ const bytes = await archive.bytes();
101
+
102
+ // As Blob
103
+ const blob = await archive.blob();
104
+
105
+ // With gzip compression
106
+ const gzippedBytes = await archive.bytes("gzip");
107
+ const gzippedBlob = await archive.blob("gzip");
108
+ ```
109
+
110
+ ## Extracting Archives
111
+
112
+ ### From Existing Archive Data
113
+
114
+ Create an archive from existing tar/tar.gz data:
115
+
116
+ ```ts
117
+ // From a file
118
+ const tarball = await Bun.file("package.tar.gz").bytes();
119
+ const archiveFromFile = Bun.Archive.from(tarball);
120
+ ```
121
+
122
+ ```ts
123
+ // From a fetch response
124
+ const response = await fetch("https://example.com/archive.tar.gz");
125
+ const archiveFromFetch = Bun.Archive.from(await response.blob());
126
+ ```
127
+
128
+ ### Extracting to Disk
129
+
130
+ Use `.extract()` to write all files to a directory:
131
+
132
+ ```ts
133
+ const tarball = await Bun.file("package.tar.gz").bytes();
134
+ const archive = Bun.Archive.from(tarball);
135
+ const count = await archive.extract("./extracted");
136
+ console.log(`Extracted ${count} entries`);
137
+ ```
138
+
139
+ The target directory is created automatically if it doesn't exist. Existing files are overwritten. The returned count includes files, directories, and symlinks (on POSIX systems).
140
+
141
+ **Note**: On Windows, symbolic links in archives are always skipped during extraction. Bun does not attempt to create them regardless of privilege level. On Linux and macOS, symlinks are extracted normally.
142
+
143
+ **Security note**: Bun.Archive validates paths during extraction, rejecting absolute paths (POSIX `/`, Windows drive letters like `C:\` or `C:/`, and UNC paths like `\\server\share`). Path traversal components (`..`) are normalized away (e.g., `dir/sub/../file` becomes `dir/file`) to prevent directory escape attacks.
144
+
145
+ ### Filtering Extracted Files
146
+
147
+ Use glob patterns to extract only specific files. Patterns are matched against archive entry paths normalized to use forward slashes (`/`). Positive patterns specify what to include, and negative patterns (prefixed with `!`) specify what to exclude. Negative patterns are applied after positive patterns, so **using only negative patterns will match nothing** (you must include a positive pattern like `**` first):
148
+
149
+ ```ts
150
+ const tarball = await Bun.file("package.tar.gz").bytes();
151
+ const archive = Bun.Archive.from(tarball);
152
+
153
+ // Extract only TypeScript files
154
+ const tsCount = await archive.extract("./extracted", { glob: "**/*.ts" });
155
+
156
+ // Extract files from multiple directories
157
+ const multiCount = await archive.extract("./extracted", {
158
+ glob: ["src/**", "lib/**"],
159
+ });
160
+ ```
161
+
162
+ Use negative patterns (prefixed with `!`) to exclude files. When mixing positive and negative patterns, entries must match at least one positive pattern and not match any negative pattern:
163
+
164
+ ```ts
165
+ // Extract everything except node_modules
166
+ const distCount = await archive.extract("./extracted", {
167
+ glob: ["**", "!node_modules/**"],
168
+ });
169
+
170
+ // Extract source files but exclude tests
171
+ const srcCount = await archive.extract("./extracted", {
172
+ glob: ["src/**", "!**/*.test.ts", "!**/__tests__/**"],
173
+ });
174
+ ```
175
+
176
+ ## Reading Archive Contents
177
+
178
+ ### Get All Files
179
+
180
+ Use `.files()` to get archive contents as a `Map` of `File` objects without extracting to disk. Unlike `extract()` which processes all entry types, `files()` returns only regular files (no directories):
181
+
182
+ ```ts
183
+ const tarball = await Bun.file("package.tar.gz").bytes();
184
+ const archive = Bun.Archive.from(tarball);
185
+ const files = await archive.files();
186
+
187
+ for (const [path, file] of files) {
188
+ console.log(`${path}: ${file.size} bytes`);
189
+ console.log(await file.text());
190
+ }
191
+ ```
192
+
193
+ Each `File` object includes:
194
+
195
+ - `name` - The file path within the archive (always uses forward slashes `/` as separators)
196
+ - `size` - File size in bytes
197
+ - `lastModified` - Modification timestamp
198
+ - Standard `Blob` methods: `text()`, `arrayBuffer()`, `stream()`, etc.
199
+
200
+ **Note**: `files()` loads file contents into memory. For large archives, consider using `extract()` to write directly to disk instead.
201
+
202
+ ### Error Handling
203
+
204
+ Archive operations can fail due to corrupted data, I/O errors, or invalid paths. Use try/catch to handle these cases:
205
+
206
+ ```ts
207
+ try {
208
+ const tarball = await Bun.file("package.tar.gz").bytes();
209
+ const archive = Bun.Archive.from(tarball);
210
+ const count = await archive.extract("./output");
211
+ console.log(`Extracted ${count} entries`);
212
+ } catch (e: unknown) {
213
+ if (e instanceof Error) {
214
+ const error = e as Error & { code?: string };
215
+ if (error.code === "EACCES") {
216
+ console.error("Permission denied");
217
+ } else if (error.code === "ENOSPC") {
218
+ console.error("Disk full");
219
+ } else {
220
+ console.error("Archive error:", error.message);
221
+ }
222
+ } else {
223
+ console.error("Archive error:", String(e));
224
+ }
225
+ }
226
+ ```
227
+
228
+ Common error scenarios:
229
+
230
+ - **Corrupted/truncated archives** - `Archive.from()` loads the archive data; errors may be deferred until read/extract operations
231
+ - **Permission denied** - `extract()` throws if the target directory is not writable
232
+ - **Disk full** - `extract()` throws if there's insufficient space
233
+ - **Invalid paths** - Operations throw for malformed file paths
234
+
235
+ The count returned by `extract()` includes all successfully written entries (files, directories, and symlinks on POSIX systems).
236
+
237
+ **Security note**: Bun.Archive automatically validates paths during extraction. Absolute paths (POSIX `/`, Windows drive letters, UNC paths) and unsafe symlink targets are rejected. Path traversal components (`..`) are normalized away to prevent directory escape.
238
+
239
+ For additional security with untrusted archives, you can enumerate and validate paths before extraction:
240
+
241
+ ```ts
242
+ const archive = Bun.Archive.from(untrustedData);
243
+ const files = await archive.files();
244
+
245
+ // Optional: Custom validation for additional checks
246
+ for (const [path] of files) {
247
+ // Example: Reject hidden files
248
+ if (path.startsWith(".") || path.includes("/.")) {
249
+ throw new Error(`Hidden file rejected: ${path}`);
250
+ }
251
+ // Example: Whitelist specific directories
252
+ if (!path.startsWith("src/") && !path.startsWith("lib/")) {
253
+ throw new Error(`Unexpected path: ${path}`);
254
+ }
255
+ }
256
+
257
+ // Extract to a controlled destination
258
+ await archive.extract("./safe-output");
259
+ ```
260
+
261
+ When using `files()` with a glob pattern, an empty `Map` is returned if no files match:
262
+
263
+ ```ts
264
+ const matches = await archive.files("*.nonexistent");
265
+ if (matches.size === 0) {
266
+ console.log("No matching files found");
267
+ }
268
+ ```
269
+
270
+ ### Filtering with Glob Patterns
271
+
272
+ Pass a glob pattern to filter which files are returned:
273
+
274
+ ```ts
275
+ // Get only TypeScript files
276
+ const tsFiles = await archive.files("**/*.ts");
277
+
278
+ // Get files in src directory
279
+ const srcFiles = await archive.files("src/*");
280
+
281
+ // Get all JSON files (recursive)
282
+ const jsonFiles = await archive.files("**/*.json");
283
+
284
+ // Get multiple file types with array of patterns
285
+ const codeFiles = await archive.files(["**/*.ts", "**/*.js"]);
286
+ ```
287
+
288
+ Supported glob patterns (subset of [Bun.Glob](/docs/api/glob) syntax):
289
+
290
+ - `*` - Match any characters except `/`
291
+ - `**` - Match any characters including `/`
292
+ - `?` - Match single character
293
+ - `[abc]` - Match character set
294
+ - `{a,b}` - Match alternatives
295
+ - `!pattern` - Exclude files matching pattern (negation). Must be combined with positive patterns; using only negative patterns matches nothing.
296
+
297
+ See [Bun.Glob](/docs/api/glob) for the full glob syntax including escaping and advanced patterns.
298
+
299
+ ## Compression
300
+
301
+ Bun.Archive supports gzip compression for both reading and writing:
302
+
303
+ ```ts
304
+ // Reading: automatically detects gzip
305
+ const gzippedTarball = await Bun.file("archive.tar.gz").bytes();
306
+ const archive = Bun.Archive.from(gzippedTarball);
307
+
308
+ // Writing: specify compression
309
+ const files = { "hello.txt": "Hello, World!" };
310
+ await Bun.Archive.write("output.tar.gz", files, "gzip");
311
+
312
+ // Getting bytes: specify compression
313
+ const gzippedBytes = await archive.bytes("gzip");
314
+ ```
315
+
316
+ The compression argument accepts:
317
+
318
+ - `"gzip"` - Enable gzip compression
319
+ - `true` - Same as `"gzip"`
320
+ - `false` or `undefined` - No compression
321
+
322
+ ## Examples
323
+
324
+ ### Bundle Project Files
325
+
326
+ ```ts
327
+ import { Glob } from "bun";
328
+
329
+ // Collect source files
330
+ const files: Record<string, string> = {};
331
+ const glob = new Glob("src/**/*.ts");
332
+
333
+ for await (const path of glob.scan(".")) {
334
+ // Normalize path separators to forward slashes for cross-platform compatibility
335
+ const archivePath = path.replaceAll("\\", "/");
336
+ files[archivePath] = await Bun.file(path).text();
337
+ }
338
+
339
+ // Add package.json
340
+ files["package.json"] = await Bun.file("package.json").text();
341
+
342
+ // Create compressed archive
343
+ await Bun.Archive.write("bundle.tar.gz", files, "gzip");
344
+ ```
345
+
346
+ ### Extract and Process npm Package
347
+
348
+ ```ts
349
+ const response = await fetch("https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz");
350
+ const archive = Bun.Archive.from(await response.blob());
351
+
352
+ // Get package.json
353
+ const files = await archive.files("package/package.json");
354
+ const packageJson = files.get("package/package.json");
355
+
356
+ if (packageJson) {
357
+ const pkg = JSON.parse(await packageJson.text());
358
+ console.log(`Package: ${pkg.name}@${pkg.version}`);
359
+ }
360
+ ```
361
+
362
+ ### Create Archive from Directory
363
+
364
+ ```ts
365
+ import { readdir } from "node:fs/promises";
366
+ import { join } from "node:path";
367
+
368
+ async function archiveDirectory(dir: string): Promise<Bun.Archive> {
369
+ const files: Record<string, Blob> = {};
370
+
371
+ async function walk(currentDir: string, prefix: string = "") {
372
+ const entries = await readdir(currentDir, { withFileTypes: true });
373
+
374
+ for (const entry of entries) {
375
+ const fullPath = join(currentDir, entry.name);
376
+ const archivePath = prefix ? `${prefix}/${entry.name}` : entry.name;
377
+
378
+ if (entry.isDirectory()) {
379
+ await walk(fullPath, archivePath);
380
+ } else {
381
+ files[archivePath] = Bun.file(fullPath);
382
+ }
383
+ }
384
+ }
385
+
386
+ await walk(dir);
387
+ return Bun.Archive.from(files);
388
+ }
389
+
390
+ const archive = await archiveDirectory("./my-project");
391
+ await Bun.Archive.write("my-project.tar.gz", archive, "gzip");
392
+ ```
393
+
394
+ ## Reference
395
+
396
+ > **Note**: The following type signatures are simplified for documentation purposes. See [`packages/bun-types/bun.d.ts`](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts) for the full type definitions.
397
+
398
+ ```ts
399
+ type ArchiveCompression = "gzip" | boolean;
400
+
401
+ type ArchiveInput =
402
+ | Record<string, string | Blob | Bun.ArrayBufferView | ArrayBufferLike>
403
+ | Blob
404
+ | Bun.ArrayBufferView
405
+ | ArrayBufferLike;
406
+
407
+ interface ArchiveExtractOptions {
408
+ /** Glob pattern(s) to filter extraction. Supports negative patterns with "!" prefix. */
409
+ glob?: string | readonly string[];
410
+ }
411
+
412
+ class Archive {
413
+ /**
414
+ * Create an Archive from input data
415
+ */
416
+ static from(data: ArchiveInput): Archive;
417
+
418
+ /**
419
+ * Write an archive directly to disk
420
+ */
421
+ static write(path: string, data: ArchiveInput | Archive, compress?: ArchiveCompression): Promise<void>;
422
+
423
+ /**
424
+ * Extract archive to a directory
425
+ * @returns Number of entries extracted (files, directories, and symlinks)
426
+ */
427
+ extract(path: string, options?: ArchiveExtractOptions): Promise<number>;
428
+
429
+ /**
430
+ * Get archive as a Blob
431
+ */
432
+ blob(compress?: ArchiveCompression): Promise<Blob>;
433
+
434
+ /**
435
+ * Get archive as a Uint8Array
436
+ */
437
+ bytes(compress?: ArchiveCompression): Promise<Uint8Array<ArrayBuffer>>;
438
+
439
+ /**
440
+ * Get archive contents as File objects (regular files only, no directories)
441
+ */
442
+ files(glob?: string | readonly string[]): Promise<Map<string, File>>;
443
+ }
444
+ ```
@@ -358,6 +358,8 @@ Bun represents [pointers](<https://en.wikipedia.org/wiki/Pointer_(computer_progr
358
358
 
359
359
  **Why not `BigInt`?** `BigInt` is slower. JavaScript engines allocate a separate `BigInt` which means they can't fit into a regular JavaScript value. If you pass a `BigInt` to a function, it will be converted to a `number`
360
360
 
361
+ **Windows Note**: The Windows API type HANDLE does not represent a virtual address, and using `ptr` for it will _not_ work as expected. Use `u64` to safely represent HANDLE values.
362
+
361
363
  </Accordion>
362
364
 
363
365
  To convert from a `TypedArray` to a pointer:
package/package.json CHANGED
@@ -33,5 +33,5 @@
33
33
  "bun.js",
34
34
  "types"
35
35
  ],
36
- "version": "1.3.6-canary.20260108T140918"
36
+ "version": "1.3.6-canary.20260110T140659"
37
37
  }