Skip to content

refactor(zip): replace fflate with adm-zip for opossum file handling#3164

Open
abraemer wants to merge 1 commit into
mainfrom
feat/adm-zip-migration
Open

refactor(zip): replace fflate with adm-zip for opossum file handling#3164
abraemer wants to merge 1 commit into
mainfrom
feat/adm-zip-migration

Conversation

@abraemer

Copy link
Copy Markdown
Contributor

Summary

Replace fflate with adm-zip as the zip library for .opossum file reading and writing.

Problem

The full decompressed input.json bytes (storedInputFileRaw) were held in memory for the entire session so they could be written back verbatim on each save. On every save, the entire zip was recreated from scratch via fflate.zip(), recompressing both input.json and output.json.

Solution

adm-zip's updateFile() allows modifying individual zip entries in-place. Unchanged entries (like input.json) pass through with their original compressed bytes intact — no decompression or recompression needed.

Key changes:

  • opossumArchive.ts: Use AdmZip instead of fflate streaming unzip; return the AdmZip instance for later reuse on save
  • write-file.ts: Use AdmZip.updateFile() for saves — only output.json is recompressed; input.json compressed bytes pass through unchanged; writeOpossumFile is now synchronous
  • parseFile.ts: Replace fflate.strFromU8() with Buffer.toString()
  • loadFile.ts / dbProcess.ts: Replace storedInputFileRaw (Uint8Array) with storedOpossumZip (AdmZip instance)
  • types.ts: Replace inputFileRaw with opossumZip in ParsedOpossumInputAndOutput
  • LegacyFileConverter.ts / fixtures.ts: Remove await from now-synchronous writeOpossumFile calls
  • Remove fflate dependency, add adm-zip + @types/adm-zip

Compression level note

adm-zip uses Node.js zlib with the default compression level (6), whereas fflate was configured at level 5. The practical difference is negligible (~0.1% size difference).

What stays

stream-json + bytesAsStream() + parseJsonStream() remain — they are still needed for parsing large input JSON to avoid V8's ~512 MB string-length cap.

Checklist

  • All 856 unit tests pass
  • Typecheck clean
  • Lint clean
  • Format clean
  • No circular imports
  • Knip (unused code) clean

@socket-security

socket-security Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​adm-zip@​0.5.81001007581100
Addedadm-zip@​0.5.171001009781100

View full report

@abraemer abraemer force-pushed the feat/adm-zip-migration branch from 1f92bfa to 083fe2c Compare June 17, 2026 12:11
Replace fflate with adm-zip as the zip library for .opossum file
reading and writing. This eliminates the need to hold the full
decompressed input.json bytes (storedInputFileRaw) in memory for
the entire session.

Key changes:
- opossumArchive.ts: use AdmZip instead of fflate streaming unzip;
  return the AdmZip instance so it can be reused for in-place saves
- write-file.ts: use AdmZip.updateFile() for saves (only output.json
  is recompressed; input.json compressed bytes pass through unchanged);
  writeOpossumFile is now synchronous
- parseFile.ts: replace fflate.strFromU8 with Buffer.toString
- loadFile.ts/dbProcess.ts: replace storedInputFileRaw (Uint8Array)
  with storedOpossumZip (AdmZip instance)
- types.ts: replace inputFileRaw with opossumZip in
  ParsedOpossumInputAndOutput
- LegacyFileConverter.ts/fixtures.ts: remove await from now-sync
  writeOpossumFile calls
- Remove fflate dependency, add adm-zip + @types/adm-zip

Signed-off-by: Adrian Braemer <adrian.braemer@tngtech.com>
@abraemer abraemer force-pushed the feat/adm-zip-migration branch from 083fe2c to 1c87228 Compare June 17, 2026 12:25
@abraemer abraemer marked this pull request as ready for review June 17, 2026 14:17
@mariusAsaTNG

Copy link
Copy Markdown
Contributor

awesome idea, I was trying to do this but never found the right package. Did not know about adm-zip

@mariusAsaTNG

Copy link
Copy Markdown
Contributor

Some numbers would have been nice. On the facebook file with some manual testing I can see that the zipping time goes from ~4s -> ~1s

Comment thread src/shared/write-file.ts
if (output) {
zip.addFile(OUTPUT_FILE_NAME, toBuffer(output));
}
zip.writeZip(path);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: you can move this before the return, since in all branches we want to write the Zip

Comment thread src/shared/write-file.ts
if (output) {
zip.updateFile(OUTPUT_FILE_NAME, toBuffer(output));
}
zip.writeZip(path);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of writeZip I would use await zip.writeZipPromise(path), since this is non blocking. Perhaps there is a reason why you decided to go the sync route?
Leaving it async would minimize the blast radius of this PR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh good catch 👍


it('writes to opossumFilePath as .opossum format', async () => {
const inputFileRaw = new Uint8Array([1, 2, 3]);
const opossumZip = new AdmZip();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably no reason, but why did the Uint8Array contain [1, 2, 3]?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea 😅

@abraemer

abraemer commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Some numbers would have been nice. On the facebook file with some manual testing I can see that the zipping time goes from ~4s -> ~1s

Thanks for timing it. I did this on the train and didn't have any sufficiently large opossum file at hand

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants