January 13, 2026·4 min read·engineeringtypescriptbuntooling

Bun 1.3.6 puts archives and commented configuration in the runtime

Bun adds tar archive operations and JSONC parsing without separate packages. Its TypeScript declarations preserve a useful boundary between parsing configuration and validating its shape.

Bun 1.3.6, released on January 13, adds native archive operations and a parser for JSON with comments. These are ordinary tooling tasks that otherwise need another dependency or an external command. The interesting design choice is how the APIs fit into existing JavaScript values while preserving distinctions that TypeScript callers still need.

My view of TypeScript is mixed, but the quality of its web ecosystem is difficult to dismiss. Good interfaces depend on careful implementation, and the tools that build those interfaces deserve the same attention. A runtime that handles routine build work through a small API can remove integration work. The benefit depends on the contract being clear enough to replace the dependency responsibly.

The declarations shipped with Bun 1.3.6 provide that detail. Bun.Archive accepts a collection of named contents or existing archive bytes. Bun.JSONC.parse() accepts a string and returns unknown. One produces a structured archive object, while the other deliberately makes no claim about the application's configuration schema.

Comment syntax is separate from configuration meaning

The JSONC parser supports single-line comments, block comments, and trailing commas in arrays and objects. That matches common configuration conventions, including the kind of commented files used by TypeScript tooling. Reading such a file no longer requires pretending it is strict JSON or removing comments through an improvised text replacement.

The important part for a TypeScript program is the declared result. An unknown value cannot simply be treated as a configuration object with a particular field. The parser understands the input grammar. It does not know whether a field is required by the application or whether a number is acceptable for that field.

This example makes the boundary explicit. The parser accepts the comments and trailing comma, while the surrounding code checks the value it intends to use.

const config = Bun.JSONC.parse(`{
  // Relative output directory
  "outdir": "dist",
}`)

if (
  typeof config !== "object" ||
  config === null ||
  !("outdir" in config) ||
  typeof config.outdir !== "string"
) {
  throw new Error("Expected a string outdir")
}

console.log(config.outdir)

The check is intentionally small because the example consumes one field. A real configuration format may need a separate schema implementation. The runtime parser removes the need for a JSONC grammar dependency, but the application's required properties remain an application decision.

That division is useful to me because it gives the type checker honest information. A cast to a configuration interface would only assert what the caller hopes the document contains. Returning unknown makes the missing validation visible where the parsed data enters typed application code.

Archives use familiar data containers

The archive constructor accepts an object whose keys are archive paths and whose values contain the corresponding file data. The versioned declarations support strings, blobs, and binary buffers as content. Gzip compression is optional, so creating a tar archive and compressing it are separate choices.

const archive = new Bun.Archive(
  {
    "manifest.json": JSON.stringify({ format: 1 }),
    "payload.txt": "Example payload\n",
  },
  { compress: "gzip" },
)

await Bun.write("bundle.tar.gz", archive)

This is an example of assembling known data into a bundle. It does not run a directory traversal or infer which project files belong in the archive. The object passed to the constructor supplies that membership explicitly. A tool can construct it from its own rules without invoking a shell archiver.

The archive API documentation describes reading contents without extraction as well as extracting them to disk. The versioned declarations confirm that files() returns a map of File objects for regular files and loads their contents into memory. extract() instead writes entries to a destination and returns their count.

Those operations have different consequences despite starting from the same archive. Reading a manifest in memory does not require writing every entry into a working directory. Extracting a bundle performs filesystem operations and overwrites existing files according to the declared API. Choosing between them expresses what the calling tool actually needs.

The release announcement says asynchronous archive operations use Bun's worker pool. That concerns where the runtime performs the work. It does not make loading an archive's contents into memory costless. The API's value is that the program can select a representation appropriate to its next operation.

The same archive can provide a Blob or a Uint8Array through asynchronous methods. A tool that needs to pass the bundle to another JavaScript API can use those values instead of introducing an intermediate filesystem path solely to transfer bytes between operations.

Integration reduces one kind of dependency

Both additions make routine tooling code shorter, but the runtime now owns the parser and archive implementation. Projects using these names depend on Bun's API and version instead of a separate package implementing the same operation. That is a reasonable tradeoff when Bun is already an intentional part of the toolchain.

The TypeScript declarations make the resulting obligations concrete. Parsed configuration still requires shape validation. Reading all archive files materializes their contents, and extraction writes to a destination. Removing an installation dependency is useful when those remaining operations are clear to the caller. Bun 1.3.6 provides that smaller implementation boundary for scripts that already target its runtime.