1.0.8 • Published 4 years ago

bua v1.0.8

Weekly downloads
5
License
MIT
Repository
github
Last release
4 years ago

bua

Backup archive, as a tar alternative.

Installation

npm i bua

Header

bua's format is relatively simple, header contains the following:

HeaderOptionalValueDescription
namefalsestringBasically relative path of this directory or file
sizefalsenumberSize in bytes of this file, directory is 0
mtimetruenumberLast modified time in ms
modetruenumberFile mode, usually permission
typefalsefile | directoryType is either file or directory

Usage

bua uses stream on both packing and extracting, it is the most efficient way to transform and transmit data. Also allow you to pipe bua stream to fs, cipher, socket, etc. easily.

Pack

Here is a simple example of packing node_modules to a single .bua file.

import * as bua from 'bua';
import * as fs from 'fs';

const pack = new bua.Pack();

LetsPack('node_modules', (err) => {
    if (err) return console.log(err);
    pack.finalize(); // emit eof to the stream
});

//pipe output stream to a fs writeStream
pack.output.pipe(fs.createWriteStream('node_modules.bua')).on('finish', () => {
    console.log('packed');
});

function LetsPack(path, cb) {
    fs.stat(path, (err, stats) => {
        if (err) return cb(err);
        if (stats.isDirectory()) {
            // due to there is no data to write for a directory, 
            // you can simply `writeHeader` without creating a stream
            pack.writeHeader({
                name: path,
                size: 0,
                mtime: Math.floor(stats.mtimeMs),
                mode: stats.mode,
                type: 'directory'
            });
            // read directory recursively
            fs.readdir(path, (err, files) => {
                if (err)
                    return cb(err);
                const l = files.length;
                (function next(i = 0) {
                    if (i === l)
                        return cb(null);
                    LetsPack(PATH.join(path, files[i]), (err) => {
                        if (err)
                            return cb(err);
                        next(i + 1);
                    });
                })();
            });
        }
        else if (stats.isFile())
            // pipe the original file to `pack.entry` with header
            fs.createReadStream(path)
                .pipe(pack.entry({
                name: path,
                size: stats.size,
                mtime: Math.floor(stats.mtimeMs),
                mode: stats.mode,
                type: 'file'
            }, cb));
        else cb(null);
    });
}

Extract

Extract a .bua file to file system with original folder structure.

extract = new bua.Extract();

// callback on entry found
extract.entry((header, stream, next) => {
    const time = new Date(header.mtime);
    header.name = header.name.replace(/^node_modules/, 'node_modules_bua');
    if (header.type === 'file')
        // pipe original data to fs writeStream
        stream.pipe(fs.createWriteStream(header.name, { mode: header.mode || 0o644 })).on('finish', () => 
            // restore mtime
            fs.utimes(header.name, 0, time, (err) => {
                if (err) return next(err);
                next();
            })
        );
    else
        (function dirHandler() {

            // to prevent mkdir finished before stream is drained and call `next` too early
            let dirMked = false,
                streamDrained = false;

            // `stream.skip` to drain the stream since there is no data to write for a directory 
            stream.skip(() => {
                // if mkdir has finished then `next` 
                if (dirMked) return next();
                streamDrained = true;
            });

            fs.mkdir(header.name, { recursive: true, mode: header.mode || 0o755 }, (err) => {
                if (err) return next(err);
                // restore mtime
                fs.utimes(header.name, 0, time, (err) => {
                    if (err) return next(err);
                    // if stream has drained then `next` 
                    if (streamDrained) return next();
                    dirMked = true;
                });
            });
        })();
});


//pipe fs readStream to input stream
fs.createReadStream('node_modules.bua').pipe(extract.input).on('finish', () => {
    console.log('extracted', PATH.resolve('node_modules_bua'));
});
1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago