CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tar-stream

Streaming tar parser and generator that operates purely using streams without hitting the file system.

Overview
Eval results
Files

packing.mddocs/

Tar Packing

Streaming tar archive generator that creates tar data from entries without hitting the file system. The pack stream allows adding files, directories, and other entry types to create complete tar archives.

Capabilities

Pack Factory Function

Creates a new tar packing stream.

/**
 * Creates a new tar packing stream
 * @param {PackOptions} [opts] - Optional configuration
 * @returns {Pack} Pack stream instance
 */
function pack(opts?: PackOptions): Pack;

interface PackOptions {
  // No specific options currently defined
}

Pack Stream

Readable stream that generates tar data from added entries.

class Pack extends Readable {
  /**
   * Add an entry to the tar archive
   * @param {Header} header - Entry metadata
   * @param {Buffer|string} [buffer] - Entry content (for files)
   * @param {Function} [callback] - Called when entry is added
   * @returns {EntryWritableStream} Stream for writing entry content
   */
  entry(header: Header, buffer?: Buffer | string, callback?: (err?: Error) => void): EntryWritableStream;

  /**
   * Finalize the tar archive - must be called when done adding entries
   */
  finalize(): void;
}

Entry Writable Stream

Writable stream for adding content to a tar entry.

class EntryWritableStream extends Writable {
  /** Number of bytes written to this entry */
  written: number;
  /** Header for this entry */
  header: Header;
}

Usage Examples

Basic Packing

const tar = require('tar-stream');
const fs = require('fs');

const pack = tar.pack();

// Add a simple text file
pack.entry({ name: 'hello.txt' }, 'Hello World!');

// Add a file with metadata
pack.entry({
  name: 'readme.md',
  size: 13,
  mode: 0o644,
  mtime: new Date(),
  type: 'file'
}, 'Hello README!');

// Finalize the archive
pack.finalize();

// Save to file
pack.pipe(fs.createWriteStream('archive.tar'));

Streaming Entry Content

const tar = require('tar-stream');

const pack = tar.pack();

// Add a file with streaming content
const entry = pack.entry({
  name: 'large-file.txt',
  size: 11 // must specify size for streaming
}, function(err) {
  if (err) throw err;
  console.log('Entry added');
  pack.finalize();
});

// Write content to the entry stream
entry.write('Hello ');
entry.write('World!');
entry.end();

pack.pipe(process.stdout);

Directory Entries

const tar = require('tar-stream');

const pack = tar.pack();

// Add a directory
pack.entry({
  name: 'my-directory/',
  type: 'directory',
  mode: 0o755
});

// Add files within the directory
pack.entry({
  name: 'my-directory/file1.txt'
}, 'File 1 content');

pack.entry({
  name: 'my-directory/file2.txt'
}, 'File 2 content');

pack.finalize();

Symbolic Links

const tar = require('tar-stream');

const pack = tar.pack();

// Add a symlink
pack.entry({
  name: 'link-to-file.txt',
  type: 'symlink',
  linkname: 'original-file.txt'
});

// Add the target file
pack.entry({
  name: 'original-file.txt'
}, 'This is the original file');

pack.finalize();

Complex Metadata

const tar = require('tar-stream');

const pack = tar.pack();

pack.entry({
  name: 'important-file.txt',
  size: 0,
  mode: 0o600, // read-write for owner only
  uid: 1000,
  gid: 1000,
  uname: 'user',
  gname: 'group',
  mtime: new Date('2023-01-01'),
  type: 'file'
}, 'Sensitive content');

pack.finalize();

Modifying Existing Archives

const tar = require('tar-stream');
const fs = require('fs');

const extract = tar.extract();
const pack = tar.pack();

extract.on('entry', function(header, stream, callback) {
  // Modify the path - add 'backup/' prefix
  header.name = 'backup/' + header.name;

  // Pipe the entry to the new archive
  stream.pipe(pack.entry(header, callback));
});

extract.on('finish', function() {
  pack.finalize();
});

// Pipe old archive through extractor to new packer
fs.createReadStream('old.tar').pipe(extract);
pack.pipe(fs.createWriteStream('new.tar'));

Error Handling

const tar = require('tar-stream');

const pack = tar.pack();

pack.on('error', function(err) {
  console.error('Pack error:', err.message);
});

// Add entry with error handling
const entry = pack.entry({
  name: 'test.txt',
  size: 5 // size must match actual content
}, function(err) {
  if (err) {
    console.error('Entry error:', err.message);
    return;
  }
  console.log('Entry added successfully');
  pack.finalize();
});

entry.on('error', function(err) {
  console.error('Entry stream error:', err.message);
});

entry.write('Hello'); // exactly 5 bytes as specified
entry.end();

Important Notes

  • Size Specification: When streaming content to entries, you must specify the exact size in the header
  • Finalization: Always call finalize() when done adding entries to properly close the archive
  • Sequential Processing: Entries are processed sequentially - wait for the callback before adding the next entry
  • Content Matching: The actual content size must match the size specified in the header, or an error will occur
  • Entry Types: Support for all standard tar entry types including files, directories, symlinks, and device files
  • Header Auto-completion: Missing header fields are automatically filled with sensible defaults

Install with Tessl CLI

npx tessl i tessl/npm-tar-stream

docs

extraction.md

index.md

packing.md

tile.json