Pack and unpack `.nar` and common archives.

One archive tool for Node.js, Bun, TypeScript, and the terminal.

Create `.nar`, `.zip`, `.tar`, `.tgz`, `.tar.gz`, or `.gz` output from files, directories, or bytes already in memory. Use the same package from ESM `import`, CommonJS `require()`, or the `nar` CLI without bolting together one-format tools.

Write native and interchange archives Pack directories, files, or in-memory bytes into `.nar`, `.zip`, `.tar`, `.tgz`, `.tar.gz`, or `.gz`.
Read incoming formats Unpack `.nar`, `.zip`, `.tar`, `.tgz`, `.tar.gz`, and `.gz` through the same entry point.
One surface `pack()` and `unpack()` stay aligned across ESM, CommonJS, and the `nar` CLI.
Filesystem or memory Write to disk when you need files, or keep the result in bytes when a pipeline only needs memory output.
Explicit failures Invalid inputs and unsafe paths fail with stable `NodearchiveError` codes.

Install

Install the package for application code, or add `nar` globally when you want the command available in any shell.

Add to a project package.json
npm install @nodearchive/nodearchive
pnpm add @nodearchive/nodearchive
yarn add @nodearchive/nodearchive
bun add @nodearchive/nodearchive
Global CLI shell
npm i -g @nodearchive/nodearchive
bun add --global @nodearchive/nodearchive

CLI

Keep the command surface small: `pack` to write archives, `unpack` to extract them, and `--help` when you need the flags.

Create archives nar pack
nar pack ./src ./dist/app.nar --passThru
nar pack ./src ./dist/app.zip --outFormat zip --passThru
nar pack ./package.json ./dist/meta.nar --force
nar pack ./src ./dist/app.nar --update --passThru
Extract archives nar unpack
nar unpack ./dist/app.nar ./out --force --passThru
nar unpack ./incoming.zip ./out --force
nar unpack ./dist/app.nar ./preview --whatIf --passThru

API

The API stays small on purpose: `pack()` writes archives, `unpack()` reads them, the package works from both `import` and `require()`, and `outFormat` switches the pack target when `.nar` is not the format you want.

ESM import
import { pack, unpack } from '@nodearchive/nodearchive'

await pack({
  literalPath: ['src', 'package.json'],
  destinationPath: './dist/app.nar',
  force: true,
})

await unpack({
  path: './dist/app.nar',
  destinationPath: './out',
  force: true,
})
CommonJS require
const { pack, unpack } = require('@nodearchive/nodearchive')

async function main() {
  const archive = await pack({ blob: 'hello world' })
  const restored = await unpack({ blob: archive })

  console.log(Buffer.from(restored).toString('utf8'))
}

main()
Output formats esm + cjs
await pack({
  literalPath: ['src'],
  destinationPath: './dist/app.zip',
  outFormat: 'zip',
})

await pack({
  literalPath: ['src'],
  destinationPath: './dist/app.tgz',
})
Memory mode esm + cjs
const archive = await pack({ blob: 'hello world' })
const restored = await unpack({ blob: archive })

console.log(Buffer.from(restored).toString('utf8'))

Behavior

Path selection `path` uses glob expansion through `fast-glob`, while `literalPath` stays exact.
Input shapes In-memory mode accepts strings, blobs, typed arrays, array buffers, and shared array buffers.
Output formats and updates `outFormat` or the destination extension picks the archive type. `.gz` expects one input, `update` stays native to `.nar`, and `whatIf` plans without writing.
Safety Unsafe extraction paths and unsupported archive entry types are rejected instead of being silently rewritten.