@devstacks/packager v0.20.2
@devstacks/packager
A robust utility for archiving, compressing, and signing web applications for cross-platform deployment. Built with TypeScript and designed for both CLI and programmatic usage.
Features
- 📦 Archiving: Preserve directory structures with metadata
- 🗜️ Compression: Multiple algorithms (GZIP, Brotli, DEFLATE)
- 🔑 Cryptography: Ed25519 signing and verification
- 🖥️ CLI: Intuitive command-line interface
- ⚡ API: Fully typed, promise-based API
- 🔒 Security: Built with security best practices
- 📏 Validation: Comprehensive input validation with Zod
Table of Contents
Installation
Prerequisites
- Node.js 18.0.0 or later
- npm 7.0.0 or later
Global Installation
For CLI usage:
npm install -g @devstacks/packagerProject Installation
For programmatic usage:
npm install @devstacks/packagerUsing with npx
For one-off usage without installation:
npx @devstacks/packager [command] [options]Quick Start
- Create a package (archive + compress + sign):
# Generate a key pair first
packager generate-keys private.key public.key
# Create a package
packager package ./src ./dist/package.pkg --algorithm gzip --privkey private.key- Use in your code:
import { createPackage, CompressionAlgorithm } from '@devstacks/packager';
// Create a package programmatically
await createPackage(
'./src',
'./dist/package.pkg',
{
algorithm: CompressionAlgorithm.GZIP,
privateKeyPath: './private.key'
}
);CLI Usage
Archive Operations
Archive a Directory
# Basic usage
packager archive ./src ./output.archive
# With include/exclude patterns
packager archive ./src ./output.archive \
--include "**/*.html,**/*.js,**/*.css" \
--exclude "**/*.map,**/node_modules/**,**/.git/**"
# Shorthand
packager a ./src ./output.archiveUnarchive a File
packager unarchive ./output.archive ./extracted-filesCompression Operations
Compress a File
# Basic compression
packager compress input.txt output.txt.gz
# With specific algorithm and level (1-9)
packager compress input.txt output.txt.gz --algorithm gzip --level 9
# Archive directory before compressing
packager compress ./src output.pkg --archive
# Shorthand
packager c input.txt output.txt.gzDecompress a File
# Basic decompression
packager decompress input.txt.gz output.txt
# Auto-detect algorithm
packager decompress input.compressed output.file
# Shorthand
packager d input.txt.gz output.txtCryptography Operations
Generate Key Pair
# Generate Ed25519 key pair
packager generate-keys private.key public.key
# Shorthand
packager g private.key public.keySign a File
# Sign a file
packager sign file.txt file.txt.sig --privkey private.key
# Shorthand
packager s file.txt file.txt.sig --privkey private.keyVerify a Signature
# Verify a signature
packager verify file.txt file.txt.sig --pubkey public.key
# Shorthand
packager v file.txt file.txt.sig --pubkey public.keyComplete Packaging
Create a complete package (archive + compress + sign) in one command:
# Basic package
packager package ./src ./dist/package.pkg --algorithm gzip --privkey private.key
# Shorthand
packager pkg ./src ./dist/package.pkg --algorithm gzip --privkey private.key
# Using the 'acs' shorthand (archive + compress + sign)
packager acs ./src ./dist/package.pkg --algorithm gzip --privkey private.key
# With include/exclude patterns
packager acs ./src ./dist/package.pkg \
--algorithm gzip \
--privkey private.key \
--include "**/*.html,**/*.js,**/*.css" \
--exclude "**/*.map"API Reference
Archive
archiveDirectory(sourcePath: string, outputPath: string, options?: ArchiveOptions): Promise<void>
Archives a directory to a file.
import { archiveDirectory } from '@devstacks/packager';
await archiveDirectory('./src', './output.archive', {
include: ['**/*.js', '**/*.json'],
exclude: ['**/node_modules/**', '**/.git/**']
});Options:
include: Array of glob patterns to includeexclude: Array of glob patterns to exclude
unarchiveFile(archivePath: string, outputPath: string): Promise<void>
Extracts an archive to a directory.
import { unarchiveFile } from '@devstacks/packager';
await unarchiveFile('./output.archive', './extracted');Compression
compressFile(sourcePath: string, outputPath: string, options: CompressionOptions): Promise<void>
Compresses a file using the specified algorithm.
import { compressFile, CompressionAlgorithm } from '@devstacks/packager';
await compressFile('./input.txt', './output.txt.gz', {
algorithm: CompressionAlgorithm.GZIP,
level: 9
});Options:
algorithm: Compression algorithm (GZIP,BROTLI, orDEFLATE)level: Compression level (1-9, higher is better compression but slower)
decompressFile(sourcePath: string, outputPath: string, options?: { algorithm?: CompressionAlgorithm }): Promise<void>
Decompresses a file.
import { decompressFile } from '@devstacks/packager';
await decompressFile('./input.txt.gz', './output.txt');Cryptography
generateAndSaveKeyPair(options: KeyPairOptions): Promise<void>
Generates and saves an Ed25519 key pair.
import { generateAndSaveKeyPair } from '@devstacks/packager';
await generateAndSaveKeyPair({
privateKeyPath: './private.key',
publicKeyPath: './public.key'
});signFile(sourcePath: string, outputPath: string, options: SignOptions): Promise<void>
Signs a file.
import { signFile } from '@devstacks/packager';
await signFile('./input.txt', './input.txt.sig', {
privateKeyPath: './private.key'
});verifyFile(filePath: string, signaturePath: string, options: VerifyOptions): Promise<boolean>
Verifies a file's signature.
import { verifyFile } from '@devstacks/packager';
const isValid = await verifyFile(
'./input.txt',
'./input.txt.sig',
{ publicKeyPath: './public.key' }
);Utilities
createPackage(sourcePath: string, outputPath: string, options: PackageOptions): Promise<void>
Creates a complete package (archive + compress + sign).
import { createPackage, CompressionAlgorithm } from '@devstacks/packager';
await createPackage(
'./src',
'./dist/package.pkg',
{
algorithm: CompressionAlgorithm.GZIP,
privateKeyPath: './private.key',
include: ['**/*.js', '**/*.json'],
exclude: ['**/node_modules/**']
}
);Error Handling
All API functions throw typed errors that extend the native Error class. Always use try/catch blocks:
try {
await createPackage('./src', './dist/package.pkg', {
algorithm: CompressionAlgorithm.GZIP,
privateKeyPath: './private.key'
});
} catch (error) {
console.error('Package creation failed:', error.message);
if (error.code === 'ENOENT') {
console.error('File not found:', error.path);
}
}Performance
Compression Algorithms
| Algorithm | Best For | Compression | Speed | Native Support |
|---|---|---|---|---|
| GZIP | General use | Good | Fast | Widely supported |
| Brotli | Web content | Excellent | Medium | Modern browsers |
| DEFLATE | Legacy systems | Good | Fast | Universal |
Memory Usage
For large files, consider streaming or chunking to manage memory usage. The library handles files larger than memory by default.
Security
Key Management
- Always store private keys securely (never commit them to version control)
- Use environment variables for sensitive data:
export PRIVATE_KEY_PATH="/path/to/private.key"
packager sign file.txt file.txt.sig --privkey "$PRIVATE_KEY_PATH"Best Practices
- Always verify signatures before processing untrusted files
- Use strong compression levels (6-9) for sensitive data
- Keep your dependencies up to date
- Use the latest version of Node.js with security patches
Development
Setup
git clone https://github.com/devstacks-software-engineering/packager.git
cd packager/base
npm installBuilding
npm run buildTesting
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Run linting
npm run lint
# Run type checking
npm run typecheckLocal Development
Link the package for local development:
# Build and link
npm run build
npm link
# Now you can use it globally
packager --versionContributing
Contributions are welcome! Please read our Contributing Guidelines before submitting pull requests.
License
MIT © DevStacks CompressionAlgorithm.GZIP, { privateKeyPath: '/path/to/private.key' } );
## Archive Format
The archive format is designed to be simple, efficient, and extensible:
- **Header**: Contains signature, version, and number of entries
- **Entry Table**: Contains offsets and sizes for each entry
- **Entry Data**: Contains metadata (path, size, MIME type) and file data
## Compression Algorithms
The packager supports multiple compression algorithms. The choice of algorithm can impact both the size of the compressed data and the performance of compression and decompression. Native platform support for decompression is a key factor in selecting an algorithm for optimal performance.
### **DEFLATE**
* **Description:**
* A lossless data compression algorithm that uses a combination of LZ77 and Huffman coding. It is the core algorithm for GZIP and ZIP.
* **Best For Compression:**
* Good balance between compression ratio and speed; scenarios requiring low overhead.
* **Native Decompression Support & Best Fit for Decompression:**
* **Windows (C#/.NET):**
* Native support via `System.IO.Compression.DeflateStream`.
* Fits well when decompressing data from GZIP or ZIP archives, or when a lightweight, fast decompression is needed.
* **Linux (Python with GTK):**
* Native support via the `zlib` standard library.
* Excellent fit for general-purpose decompression where `zlib` is readily available.
* **Android (Java/Kotlin):**
* Native support via `java.util.zip.InflaterInputStream` or `java.util.zip.Inflater`.
* A good choice for decompressing ZIP files or raw DEFLATE streams.
* **iOS/macOS (Swift):**
* Native support via Apple's Compression framework (using `Algorithm.zlib` or the C-level `COMPRESSION_ZLIB`).
* Ideal for decompressing raw DEFLATE streams or as the core for handling GZIP/ZIP data.
### **GZIP**
* **Description:**
* Based on DEFLATE, it adds a header and trailer with metadata, including a CRC32 checksum for error detection. Good compression ratio and widely compatible.
* **Best For Compression:**
* General-purpose usage, especially for web content and file transfer where compatibility and integrity checks are valued.
* **Native Decompression Support & Best Fit for Decompression:**
* **Windows (C#/.NET):**
* Native support via `System.IO.Compression.GZipStream`.
* Ideal for decompressing `.gz` files or HTTP content encoded with GZIP.
* **Linux (Python with GTK):**
* Native support via the `gzip` and `zlib` standard libraries.
* Widely used and fits well for decompressing files and web data.
* **Android (Java/Kotlin):**
* Native support via `java.util.zip.GZIPInputStream`.
* Commonly used for decompressing web responses and `.gz` files.
* **iOS/macOS (Swift):**
* Native support by using Apple's Compression framework with `Algorithm.zlib` (DEFLATE) and manually handling GZIP headers/trailers, or by leveraging common system libraries that wrap zlib for GZIP.
* Fits well for decompressing `.gz` files and GZIP-encoded web data.
### **BROTLI**
* **Description:**
* A modern algorithm offering better compression ratios than GZIP, especially for text-based data.
* **Best For Compression:**
* When compression ratio is the highest priority, particularly for static web assets like HTML, CSS, and JavaScript.
* **Native Decompression Support & Best Fit for Decompression:**
* **Windows (C#/.NET):**
* Native support available (e.g., `System.IO.Compression.BrotliStream` in .NET Core/5+).
* Best fit for decompressing Brotli-encoded web content or assets where the .NET runtime provides built-in support.
* **Linux (Python with GTK):**
* Native decompression typically requires external libraries (e.g., `brotli` PyPI package).
* Fits best when the environment is set up with these dependencies and maximum decompression ratio benefits are sought.
* **Android (Java/Kotlin):**
* Native support is less direct for general app development compared to DEFLATE/GZIP; often available via WebView for web content or through JNI with Brotli libraries.
* Best fit for decompressing Brotli-encoded content within web contexts or when specifically integrating a Brotli library.
* **iOS/macOS (Swift):**
* Native support via Apple's Compression framework (using `Algorithm.brotli` or C-level `COMPRESSION_BROTLI`, available iOS 13+/macOS 10.15+; C API availability might be iOS 15+ for some uses, requiring OS version checks).
* Excellent for decompressing Brotli-encoded web content and text-heavy data directly within apps.
## Cryptography
The packager uses Ed25519 for digital signatures, which offers:
- **Small key sizes**: 32-byte public keys, 64-byte signatures
- **Fast verification**: 50-70x faster than RSA
- **High security**: Resistant to side-channel attacks
## License
MIT