{"_id":"@boy-offi9-inc/isomorphic-unzip","_rev":"3-171a53968f625de9206c87861bb50a19","name":"@boy-offi9-inc/isomorphic-unzip","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@boy-offi9-inc/isomorphic-unzip","version":"0.1.0","keywords":["unzip","zip","isomorphic","browser","node","apk","ipa","archive"],"author":{"name":"Boy Offi9"},"license":"MIT","_id":"@boy-offi9-inc/isomorphic-unzip@0.1.0","maintainers":[{"name":"boy-offi9-inc","email":"boyoffiinc@gmail.com"}],"dist":{"shasum":"5a90aeadaff052354080953fe086ba9375095de7","tarball":"https://registry.npmjs.org/@boy-offi9-inc/isomorphic-unzip/-/isomorphic-unzip-0.1.0.tgz","fileCount":11,"integrity":"sha512-wcLtnCPjIWrCEWOggcBV7SFiL2mUytk0VtSRWrJH/P9QVogoPXzjHH7ICog3SVUQ8oKJQeKnfq39rV6Vi+gycg==","signatures":[{"sig":"MEYCIQC8uh2ulkZnbGF7gCPhdl5QT6FUZfx9gLcRNZY7qmkjPgIhAMpsfwuwSEzUOr2s+XdI5kcvHK0GckK8bTk/AcZEQWKC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":18652},"main":"./src/index.js","types":"./types/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./types/index.d.ts","import":"./src/index.mjs","browser":"./src/index.browser.js","require":"./src/index.js"},"./package.json":"./package.json"},"gitHead":"b28b3c0b211387798b95afc18f20e14e97fd6c5a","scripts":{"test":"node --test"},"_npmUser":{"name":"boy-offi9-inc","email":"boyoffiinc@gmail.com"},"_npmVersion":"11.6.2","description":"Maintained drop-in replacement for the abandoned isomorphic-unzip — same API (getBuffer, string/RegExp/function matching), powered internally by fflate instead of a yauzl+zip.js split that never actually reached parity between Node and browser.","directories":{},"sideEffects":false,"_nodeVersion":"24.13.0","dependencies":{"fflate":"^0.8.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/isomorphic-unzip_0.1.0_1787046991241_0.4868674382461178","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@boy-offi9-inc/isomorphic-unzip","version":"0.2.0","keywords":["unzip","zip","isomorphic","browser","node","apk","ipa","archive"],"author":{"name":"Boy Offi9"},"license":"MIT","_id":"@boy-offi9-inc/isomorphic-unzip@0.2.0","maintainers":[{"name":"boy-offi9-inc","email":"boyoffiinc@gmail.com"}],"dist":{"shasum":"7fabc6447e2dc92c64a2e63503201f941436d19e","tarball":"https://registry.npmjs.org/@boy-offi9-inc/isomorphic-unzip/-/isomorphic-unzip-0.2.0.tgz","fileCount":10,"integrity":"sha512-zeqPnyshZaFpbUvMLgNydQ+nUfdVANcsBo80QiUwFxcFiks+rE0B+jOEZh9MCUDyBtyPV+XSoSRqXVEuCUP2zQ==","signatures":[{"sig":"MEYCIQDSxY+1+Mzq7Uy98TMnrT1SK/8gCIRayWj4Mw3gCA+HzgIhAJ2xio3kEIRmQgn51lNGazFQKK8280ukgH0ztiDMIOio","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIC51V4sOsz2DuQrAvAGtQyY1xRCTrbERoH7C8TnztzcGAiBgiWoMDXBKrGajyJFI7KqBPuKXik4TAA/h3mDkgdOpdw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":19462},"main":"./src/index.js","types":"./types/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./types/index.d.ts","import":"./src/index.mjs","browser":"./src/index.browser.js","require":"./src/index.js"},"./package.json":"./package.json"},"gitHead":"8f6b8f007e0e23138e828499e49d3435d016dba6","scripts":{"test":"node --test"},"_npmUser":{"name":"boy-offi9-inc","email":"boyoffiinc@gmail.com"},"_npmVersion":"11.6.2","description":"Maintained drop-in replacement for the abandoned isomorphic-unzip — same API (getBuffer, string/RegExp/function matching), powered internally by fflate instead of a yauzl+zip.js split that never actually reached parity between Node and browser.","directories":{},"sideEffects":false,"_nodeVersion":"24.13.0","dependencies":{"fflate":"^0.8.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/isomorphic-unzip_0.2.0_1788612147576_0.6377397777389684","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@boy-offi9-inc/isomorphic-unzip@0.3.0","dist":{"shasum":"be6b590c6489b44ddac6100dbeddfe47dcb3ef6f","tarball":"https://registry.npmjs.org/@boy-offi9-inc/isomorphic-unzip/-/isomorphic-unzip-0.3.0.tgz","fileCount":13,"integrity":"sha512-WmVsP3VYGECLy8uliy4loOrojTkbqD8IjgL/UnDJ8uO4BYXqRC91P+l0oeh+YAbbE4L9h3VrDnzNwXOKQGQPew==","signatures":[{"sig":"MEUCIDrYFLFVFzvNWm3PdnzYPBfyE25yG8xbLc19Oxz8Mv2PAiEA52tjmB7BJSHscWpdwm4kFZAPncpweQTS/B+bjrZUP5A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIF5V8UoZI9I+Fi7FmyYmDfs//UXbM8teeD8ng4Jc9dTWAiBUpQHWr7FgvbbvMu872a7jomn63ujioz81pAIg/Zl1BA=="}],"unpackedSize":33548},"main":"./src/index.js","name":"@boy-offi9-inc/isomorphic-unzip","types":"./types/index.d.ts","author":{"name":"Boy Offi9"},"engines":{"node":">=18"},"exports":{".":{"types":"./types/index.d.ts","import":"./src/index.mjs","browser":"./src/index.browser.js","require":"./src/index.js"},"./package.json":"./package.json"},"gitHead":"71ae283ed8d48b7ff6352bd34504843b50fb2938","license":"MIT","scripts":{"test":"node --test"},"version":"0.3.0","_npmUser":{"name":"boy-offi9-inc","email":"boyoffiinc@gmail.com"},"keywords":["unzip","zip","isomorphic","browser","node","apk","ipa","archive"],"_npmVersion":"11.6.2","description":"Maintained drop-in replacement for the abandoned isomorphic-unzip — same API (getBuffer, string/RegExp/function matching), powered internally by fflate instead of a yauzl+zip.js split that never actually reached parity between Node and browser.","directories":{},"maintainers":[{"name":"boy-offi9-inc","email":"boyoffiinc@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.13.0","dependencies":{"fflate":"^0.8.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/isomorphic-unzip_0.3.0_1788621919423_0.7472827017950381"}}},"time":{"created":"2026-08-18T09:56:30.961Z","modified":"2026-09-05T15:25:19.687Z","0.1.0":"2026-08-18T09:56:31.396Z","0.2.0":"2026-09-05T12:42:27.668Z","0.3.0":"2026-09-05T15:25:19.509Z"},"author":{"name":"Boy Offi9"},"license":"MIT","keywords":["unzip","zip","isomorphic","browser","node","apk","ipa","archive"],"description":"Maintained drop-in replacement for the abandoned isomorphic-unzip — same API (getBuffer, string/RegExp/function matching), powered internally by fflate instead of a yauzl+zip.js split that never actually reached parity between Node and browser.","maintainers":[{"name":"boy-offi9-inc","email":"boyoffiinc@gmail.com"}],"readme":"<div align=\"center\">\n  <img src=\"https://i.ibb.co/hxd66XLj/file-000000008f8081f4bd2ad19dc26151ca.png\" width=\"80\" alt=\"isomorphic-unzip icon\" />\n\n  # isomorphic-unzip\n\n  [![npm version](https://img.shields.io/npm/v/%40boy-offi9-inc%2Fisomorphic-unzip.svg)](https://www.npmjs.com/package/@boy-offi9-inc/isomorphic-unzip)\n  [![types](https://img.shields.io/badge/types-included-blue.svg)](https://github.com/boy-offi9-inc/isomorphic-unzip)\n  [![license](https://img.shields.io/npm/l/%40boy-offi9-inc%2Fisomorphic-unzip.svg)](./LICENSE)\n  [![node engine](https://img.shields.io/node/v/%40boy-offi9-inc%2Fisomorphic-unzip.svg)](./package.json)\n</div>\n\nA maintained drop-in replacement for [`isomorphic-unzip`](https://www.npmjs.com/package/isomorphic-unzip), which has been unmaintained since 2019 and never actually finished what it set out to do — by its own README: *\"we haven't made it totally consistent between NodeJS/browser yet, maybe later.\"*\n\nSame public API. Same call shape. Different internals: one real isomorphic implementation ([`fflate`](https://www.npmjs.com/package/fflate)) instead of gluing together two separate libraries (`yauzl` for Node, `zip.js` for browser) that never reached behavioral parity.\n\n**Why not just switch to `unzipit`/`fflate` directly instead?** You can, and for new code that's probably the better call. This package exists specifically for projects that already depend on `isomorphic-unzip`'s API and want to fix the underlying breakage (e.g. the Vite/browser-env incompatibility reported against `app-info-parser` and others) without rewriting their call sites.\n\n---\n\n## Migration from `isomorphic-unzip`\n\n```diff\n- const Unzip = require('isomorphic-unzip');\n+ const Unzip = require('@boy-offi9-inc/isomorphic-unzip');\n```\n\nThat's it for most usage. The `getBuffer` call shape is unchanged:\n\n```js\nconst unzip = new Unzip(pathOrBytesOrBlob);\n\nunzip.getBuffer(['androidmanifest.xml', 'resources.arsc'], (err, buffers) => {\n  if (err) throw err;\n  // buffers is { 'AndroidManifest.xml': Buffer, 'resources.arsc': Buffer }\n  console.log(buffers);\n});\n```\n\n**One behavior change worth knowing about:** `getEntries()` now works identically in both Node and browser. The original only supported it reliably in Node — that inconsistency was part of what made it unmaintained in the first place.\n\n---\n\n## Install\n\n```bash\nnpm install @boy-offi9-inc/isomorphic-unzip\n```\n\n## Usage\n\n### Node — file path\n\n```js\nconst Unzip = require('@boy-offi9-inc/isomorphic-unzip');\n\nconst unzip = new Unzip('/path/to/file.apk');\nunzip.getBuffer(['AndroidManifest.xml'], (err, buffers) => {\n  if (err) throw err;\n  console.log(buffers['AndroidManifest.xml'].toString());\n});\n```\n\n### Node — Buffer/Uint8Array\n\n```js\nconst bytes = fs.readFileSync('/path/to/file.apk');\nconst unzip = new Unzip(bytes);\n```\n\n### Browser — File/Blob\n\n```js\nconst unzip = new Unzip(fileInput.files[0]);\nunzip.getBuffer(['manifest.json'], (err, buffers) => {\n  if (err) throw err;\n  console.log(buffers['manifest.json']); // Uint8Array in browser\n});\n```\n\n### Promise-based (not in the original, added since it's the more natural fit today)\n\n```js\nconst buffers = await unzip.getBufferAsync(['AndroidManifest.xml']);\n```\n\n### Matching entries\n\n`whatYouNeed` accepts strings (case-insensitive exact match — matches the original's behavior), `RegExp`, or predicate functions:\n\n```js\nunzip.getBuffer([\n  'AndroidManifest.xml',        // exact (case-insensitive)\n  /^META-INF\\//,                 // RegExp\n  (name) => name.endsWith('.json'), // predicate\n], callback);\n```\n\n### Listing all entries\n\n```js\nconst entries = await unzip.getEntries(); // string[]\n```\n\n### Progress (not in the original)\n\n`getBuffer`/`getBufferAsync` accept an optional `onProgress` callback, called once per matched entry as it's decompressed. This is entry-level progress, not byte-level — fflate's sync API doesn't expose per-byte progress, so this is most useful for archives with many small entries (e.g. reporting \"3 of 12 files extracted\"), not for tracking a single large file.\n\n```js\nawait unzip.getBufferAsync(['AndroidManifest.xml', /^assets\\//], (entryName) => {\n  console.log('extracted', entryName);\n});\n```\n\n### Creating a zip (not in the original — this library was extract-only before)\n\n```js\nconst { zipEntries, zipEntriesAsync } = require('isomorphic-unzip');\n\n// callback\nzipEntries({ 'hello.txt': 'hello world' }, (err, zipBytes) => { /* ... */ });\n\n// promise, with an optional compression level (0 = store, 9 = max; default 6)\nconst zipBytes = await zipEntriesAsync({ 'hello.txt': 'hello world' }, { level: 9 });\n```\n\nEntry values can be a `string`, `Buffer`, `Uint8Array`, or `ArrayBuffer`.\n\n### Streaming large archives (not in the original)\n\n`getBuffer`/`getBufferAsync` load matched entries fully into memory. For large archives (or large individual entries — think APK/IPA-sized), `extractStream` processes the source in chunks and hands you each matched entry's data incrementally instead:\n\n```js\nawait unzip.extractStream(['AndroidManifest.xml', /^assets\\//], {\n  onEntry(name) {\n    console.log('starting', name);\n  },\n  onData(name, chunk, isLast) {\n    // handle this chunk — write it, pipe it, whatever\n  },\n  onEnd() {\n    console.log('done');\n  },\n});\n```\n\nMemory behavior depends on what you constructed `Unzip` with:\n- **File path (Node) or Blob/File (browser):** true streaming — the source itself is read in bounded chunks (`fs.createReadStream` / `Blob.stream()`), so memory use doesn't scale with archive size.\n- **Buffer/Uint8Array/ArrayBuffer:** the source is already fully in memory (you loaded it that way), but decompressed entries are still emitted incrementally via `onData` rather than all buffered into one returned object.\n\n**`extractToDir` (Node only)** builds on `extractStream` to write matched entries straight to disk, without ever holding their contents as Buffers:\n\n```js\nconst { extractToDir } = require('isomorphic-unzip');\n\nconst written = await extractToDir(unzip, [/^assets\\//], './out');\n// written: string[] — full paths of everything extracted\n```\n\nIt rejects entry names that would resolve outside the destination directory (zip-slip protection) — a zip's entry names are attacker-controllable data, and nothing in the zip format stops a name like `../../etc/passwd`. `extractToDir` isn't exported from the browser entry point, since writing to a real filesystem is inherently a Node concern.\n\n### Limitations\n\n- **No password-protected/encrypted zip support.** `fflate` doesn't implement zip encryption, so encrypted entries can't be read. If `getBuffer`/`getEntries`/`extractStream` hits one, the error message says so explicitly instead of surfacing a raw fflate parse error.\n\n---\n\n## Design notes\n\n- **Zero-config isomorphism.** One code path reads the input (file path, Buffer, Uint8Array, ArrayBuffer, or Blob/File) into bytes, then hands off to `fflate`'s `unzipSync`. No environment-specific branches in the unzip logic itself — that's the actual fix over the original.\n- **`fs` never reaches a browser bundle.** The one genuinely platform-specific piece — reading a file path in Node — lives in its own file, wired in only through the Node entry point. Bundlers that respect the `\"browser\"` export condition (Vite, webpack 5+, Rollup) never see `fs` referenced at all, so there's nothing to externalize or warn about — verified against a real Vite production build.\n- **`fflate` as the only runtime dependency.** Zero-dependency itself, actively maintained, genuinely the same implementation in Node and browser.\n- **CJS + ESM entry points**, TypeScript types included.\n- **Real, verified test suite** — every test builds an actual zip in memory via `fflate` and round-trips it through the `Unzip` class, not mocked.\n","readmeFilename":"README.md"}