{"id":31433157,"url":"https://github.com/ayuhito/modern-tar","last_synced_at":"2025-10-09T18:25:42.910Z","repository":{"id":316274278,"uuid":"1062247161","full_name":"ayuhito/modern-tar","owner":"ayuhito","description":"🗄 Zero dependency streaming tar parser and writer for every JavaScript runtime.","archived":false,"fork":false,"pushed_at":"2025-10-01T10:21:19.000Z","size":41694,"stargazers_count":75,"open_issues_count":2,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-01T12:09:54.547Z","etag":null,"topics":["cloudflare-workers","javascript","nodejs","streams","tar","tar-gz","typescript"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/modern-tar","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ayuhito.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-23T02:31:09.000Z","updated_at":"2025-10-01T11:56:26.000Z","dependencies_parsed_at":"2025-09-25T21:01:08.873Z","dependency_job_id":null,"html_url":"https://github.com/ayuhito/modern-tar","commit_stats":null,"previous_names":["ayuhito/modern-tar"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/ayuhito/modern-tar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayuhito%2Fmodern-tar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayuhito%2Fmodern-tar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayuhito%2Fmodern-tar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayuhito%2Fmodern-tar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ayuhito","download_url":"https://codeload.github.com/ayuhito/modern-tar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayuhito%2Fmodern-tar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279001940,"owners_count":26083226,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare-workers","javascript","nodejs","streams","tar","tar-gz","typescript"],"created_at":"2025-09-30T11:01:49.540Z","updated_at":"2025-10-09T18:25:42.904Z","avatar_url":"https://github.com/ayuhito.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# 🗄️ modern-tar\n\nZero-dependency, cross-platform, streaming tar archive library for every JavaScript runtime. Built with the browser-native Web Streams API for performance and memory efficiency.\n\n## Features\n\n- 🚀 **Streaming Architecture** - Supports large archives without loading everything into memory.\n- 📋 **Standards Compliant** - Full USTAR format support with PAX extensions. Compatible with GNU tar, BSD tar, and other standard implementations.\n- 🗜️ **Compression** - Includes helpers for gzip compression/decompression.\n- 📝 **TypeScript First** - Full type safety with detailed TypeDoc documentation.\n- ⚡ **Zero Dependencies** - No external dependencies, minimal bundle size.\n- 🌐 **Cross-Platform** - Works in browsers, Node.js, Cloudflare Workers, and other JavaScript runtimes.\n- 📁 **Node.js Integration** - Additional high-level APIs for directory packing and extraction.\n\n## Installation\n\n```sh\nnpm install modern-tar\n```\n\n## Usage\n\nThis package provides two entry points:\n\n- `modern-tar`: The core, cross-platform streaming API (works everywhere).\n- `modern-tar/fs`: High-level filesystem utilities for Node.js.\n\n### Core Usage\n\nThese APIs use the Web Streams API and can be used in any modern JavaScript environment.\n\n#### Simple\n\n```typescript\nimport { packTar, unpackTar } from 'modern-tar';\n\n// Pack entries into a tar buffer\nconst entries = [\n\t{ header: { name: \"file.txt\", size: 5 }, body: \"hello\" },\n\t{ header: { name: \"dir/\", type: \"directory\", size: 0 } },\n\t{ header: { name: \"dir/nested.txt\", size: 3 }, body: new Uint8Array([97, 98, 99]) } // \"abc\"\n];\n\n// Accepts string, Uint8Array, Blob, ReadableStream\u003cUint8Array\u003e and more...\nconst tarBuffer = await packTar(entries);\n\n// Unpack tar buffer into entries\nconst entries = await unpackTar(tarBuffer);\nfor (const entry of entries) {\n\tconsole.log(`File: ${entry.header.name}`);\n\tconst content = new TextDecoder().decode(entry.data);\n\tconsole.log(`Content: ${content}`);\n}\n```\n\n#### Streaming\n\n```typescript\nimport { createTarPacker, createTarDecoder } from 'modern-tar';\n\n// Create a tar packer\nconst { readable, controller } = createTarPacker();\n\n// Add entries dynamically\nconst fileStream = controller.add({\n\tname: \"dynamic.txt\",\n\tsize: 5,\n\ttype: \"file\"\n});\n\n// Write content to the stream\nconst writer = fileStream.getWriter();\nawait writer.write(new TextEncoder().encode(\"hello\"));\nawait writer.close();\n\n// When done adding entries, finalize the archive\ncontroller.finalize();\n\n// `readable` now contains the complete tar archive which can be piped or processed\nconst tarStream = readable;\n\n// Create a tar decoder\nconst decoder = createTarDecoder();\nconst decodedStream = tarStream.pipeThrough(decoder);\nfor await (const entry of decodedStream) {\n\tconsole.log(`Decoded: ${entry.header.name}`);\n\t// Process `entry.body` stream as needed\n}\n```\n\n#### Compression/Decompression (gzip)\n\n```typescript\nimport { createGzipEncoder, createTarPacker } from 'modern-tar';\n\n// Create and compress a tar archive\nconst { readable, controller } = createTarPacker();\nconst compressedStream = readable.pipeThrough(createGzipEncoder());\n\n// Add entries...\nconst fileStream = controller.add({ name: \"file.txt\", size: 5, type: \"file\" });\nconst writer = fileStream.getWriter();\nawait writer.write(new TextEncoder().encode(\"hello\"));\nawait writer.close();\ncontroller.finalize();\n\n// Upload compressed .tar.gz\nawait fetch('/api/upload', {\n  method: 'POST',\n  body: compressedStream,\n  headers: { 'Content-Type': 'application/gzip' }\n});\n```\n\n```typescript\nimport { createGzipDecoder, createTarDecoder, unpackTar } from 'modern-tar';\n\n// Download and process a .tar.gz file\nconst response = await fetch('https://api.example.com/archive.tar.gz');\nif (!response.body) throw new Error('No response body');\n\n// Buffer entire archive\nconst entries = await unpackTar(response.body.pipeThrough(createGzipDecoder()));\n\nfor (const entry of entries) {\n\tconsole.log(`Extracted: ${entry.header.name}`);\n\tconst content = new TextDecoder().decode(entry.data);\n\tconsole.log(`Content: ${content}`);\n}\n\n// Or chain decompression and tar parsing using streams\nconst entries = response.body\n  .pipeThrough(createGzipDecoder())\n  .pipeThrough(createTarDecoder());\n\nfor await (const entry of entries) {\n  console.log(`Extracted: ${entry.header.name}`);\n  // Process entry.body ReadableStream as needed\n}\n```\n\n### Node.js Filesystem Usage\n\nThese APIs use Node.js streams when interacting with the local filesystem.\n\n#### Simple\n\n```typescript\nimport { packTar, unpackTar } from 'modern-tar/fs';\nimport { createWriteStream, createReadStream } from 'node:fs';\nimport { pipeline } from 'node:stream/promises';\n\n// Pack a directory into a tar file\nconst tarStream = packTar('./my/project');\nconst fileStream = createWriteStream('./project.tar');\nawait pipeline(tarStream, fileStream);\n\n// Extract a tar file to a directory\nconst tarReadStream = createReadStream('./project.tar');\nconst extractStream = unpackTar('./output/directory');\nawait pipeline(tarReadStream, extractStream);\n```\n\n#### Filtering and Transformation\n\n```typescript\nimport { packTar, unpackTar } from 'modern-tar/fs';\nimport { createReadStream } from 'node:fs';\nimport { pipeline } from 'node:stream/promises';\n\n// Pack with filtering\nconst packStream = packTar('./my/project', {\n\tfilter: (filePath, stats) =\u003e !filePath.includes('node_modules'),\n\tmap: (header) =\u003e ({ ...header, mode: 0o644 }), // Set all files to 644\n\tdereference: true // Follow symlinks instead of archiving them\n});\n\n// Unpack with advanced options\nconst sourceStream = createReadStream('./archive.tar');\nconst extractStream = unpackTar('./output', {\n\t// Core options\n\tstrip: 1, // Remove first directory level\n\tfilter: (header) =\u003e header.name.endsWith('.js'), // Only extract JS files\n\tmap: (header) =\u003e ({ ...header, name: header.name.toLowerCase() }), // Transform names\n\n\t// Filesystem-specific options\n\tfmode: 0o644, // Override file permissions\n\tdmode: 0o755, // Override directory permissions\n\tmaxDepth: 50,  // Limit extraction depth for security (default: 1024)\n\tconcurrency: 8, // Limit concurrent filesystem operations (default: CPU cores)\n\tstreamTimeout: 10000 // Timeout after 10 seconds of inactivity (default: 5000ms)\n});\n\nawait pipeline(sourceStream, extractStream);\n```\n\n#### Archive Creation\n\n```typescript\nimport { packTarSources, type TarSource } from 'modern-tar/fs';\nimport { createWriteStream } from 'node:fs';\nimport { pipeline } from 'node:stream/promises';\n\n// Pack multiple sources\nconst sources: TarSource[] = [\n  { type: 'file', source: './package.json', target: 'project/package.json' },\n  { type: 'directory', source: './src', target: 'project/src' },\n  { type: 'content', content: 'Hello World!', target: 'project/hello.txt' },\n  { type: 'content', content: '#!/bin/bash\\necho \"Executable\"', target: 'bin/script.sh', mode: 0o755 }\n];\n\nconst archiveStream = packTarSources(sources);\nawait pipeline(archiveStream, createWriteStream('project.tar'));\n```\n\n#### Compression/Decompression (gzip)\n\n```typescript\nimport { packTar, unpackTar } from 'modern-tar/fs';\nimport { createWriteStream, createReadStream } from 'node:fs';\nimport { createGzip, createGunzip } from 'node:zlib';\nimport { pipeline } from 'node:stream/promises';\n\n// Pack directory and compress to .tar.gz\nconst tarStream = packTar('./my/project');\nawait pipeline(tarStream, createGzip(), createWriteStream('./project.tar.gz'));\n\n// Decompress and extract .tar.gz\nconst gzipStream = createReadStream('./project.tar.gz');\nawait pipeline(gzipStream, createGunzip(), unpackTar('./output'));\n```\n\n## API Reference\n\nSee the [API Reference](./REFERENCE.md).\n\n## Compatibility\n\nThe core library uses the [Web Streams API](https://caniuse.com/streams) and requires:\n\n- **Node.js**: 18.0+\n- **Browsers**: Modern browsers with Web Streams support\n  - Chrome 71+\n  - Firefox 102+\n  - Safari 14.1+\n  - Edge 79+\n\n## Acknowledgements\n\n- [`tar-stream`](https://github.com/mafintosh/tar-stream) and [`tar-fs`](https://github.com/mafintosh/tar-fs) - For the inspiration and test fixtures.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fayuhito%2Fmodern-tar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fayuhito%2Fmodern-tar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fayuhito%2Fmodern-tar/lists"}