GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — fix: replace @eggjs/yauzl with upstream yauzl 3.4.0, update yazl to 3.3.1 by fengmk2 · Pull Request #145 · node-modules/compressing Conversation
|
| @eggjs/yauzl 2.11.0 | upstream 3.4.0 | |
|---|---|---|
| entries | 31 | 31 |
fileName is Buffer |
31 | 31 |
externalFileAttributes present |
31 | 31 |
| files read | 21 | 21 |
leading / entry |
preserved | preserved |
Only visible difference: yauzl 3 capitalises the "End of central directory record signature not found" message, so that assertion is now case-insensitive.
yazl 3 turns "add entries after calling end()" from a tolerated no-op into a thrown error, and compressing trips it immediately.
_onEntryFinish() finalizes as soon as the entry queue is momentarily empty. For zip the finish callback runs synchronously, so a caller doing:
zipStream.addEntry(streamA, ...); zipStream.addEntry(bufferB, ...);
closed the archive after the first entry, and the second threw. Tar avoids it only because its fs.stat makes the callback async, which lets the later entries queue first.
Worth being precise about the old behaviour: yazl 2 did not drop those entries. I checked, and the produced archive contained all of them. So this was latent, not a live data-loss bug.
Fix is to finalize on the next tick and skip it if an entry arrived meanwhile. Verified the produced archive still contains every entry.
Residual limitation, unchanged in spirit from before: entries added after a longer async gap still finalize early. That is the existing drain heuristic, and giving the stream an explicit "done adding" call would be an API change worth doing separately.
171 passing on both Node 24 and Node 26, lint and tsc clean. On Node 26 the zip suite finishes in ~495ms where it previously hung for 60s. The symlink cases from #140 were re-checked through the new zip path and still block.
Drops fd-slicer2 and buffer-crc32 from the tree. Supersedes #132.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Warning Review limit reached
Next review available in: 27 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe ZIP uncompression code now uses the ChangesZIP yauzl migration
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@ ## master #145 +/- ## ========================================== + Coverage 97.15% 97.18% +0.02% ========================================== Files 19 19 Lines 1197 1207 +10 Branches 309 310 +1 ========================================== + Hits 1163 1173 +10 Misses 34 34 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This PR addresses Node 26 CI hangs in ZIP extraction by replacing the @eggjs/yauzl fork (and its problematic transitive fd-slicer2 dependency) with upstream [email protected], and adjusting the affected test expectation to be robust to an upstream error-message capitalization change.
Changes:
@eggjs/yauzl with upstream yauzl@^3.4.0 in dependencies.yauzl instead of the fork.Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
package.json |
Swaps @eggjs/yauzl for upstream yauzl@^3.4.0 to avoid Node 26 stream hang via transitive deps. |
lib/zip/uncompress_stream.js |
Updates the yauzl import to use the upstream package. |
test/zip/uncompress_stream.test.js |
Makes the ZIP-format error assertion case-insensitive to accommodate upstream message formatting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Line 42: Regenerate pnpm-lock.yaml from the updated yauzl dependency
declaration so the lockfile resolves yauzl ^3.4.0 instead of the stale
`@eggjs/yauzl` 2.11.0 entry, while preserving the dependency required by
lib/zip/uncompress_stream.js.
Fix all unresolved CodeRabbit comments on this PR:
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 2549f4c0-a10d-4063-8d61-2c032b88573b
lib/zip/uncompress_stream.jspackage.jsontest/zip/uncompress_stream.test.js
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
89baf57 to
ae7e238
Compare
The fork depends on fd-slicer2, whose ReadStream loses data when piped on Node.js 26: any zip entry over 64 KiB delivers roughly the first chunk and then stalls with no end, no error, and no close. That is why zip.uncompress() hangs until the test timeout on Node 26 while passing on 18 through 24. Upstream yauzl 3.4.0 dropped fd-slicer entirely, its only dependency now being pend, and does not have the bug. Verified identical behaviour on the contain-absolute-path.zip fixture that motivated the fork in the first place: 31 entries, Buffer fileNames under decodeStrings:false, externalFileAttributes intact, and the leading "/" entry still read. Suite is 171 passing on both Node 24 and Node 26. The only visible difference is that yauzl 3.x capitalises the "end of central directory record signature not found" message, so that assertion is now case-insensitive. Reported upstream at node-modules/yauzl#3.
yazl 3 turns "add entries after calling end()" from a tolerated no-op into a thrown error. compressing hits it because _onEntryFinish() finalizes as soon as the queue is momentarily empty, and for zip the finish callback runs synchronously, so a caller adding entries back to back closed the archive after the first one. yazl 2 accepted the later entries anyway and produced a correct archive, which is why this never surfaced. Finalize on the next tick instead, and skip it if an entry arrived in the meantime. Verified the produced archive still contains every entry.
2078c1a to
35ab10c
Compare
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
lib/tar/stream.js:138
_onEntryFinish() schedules a new setImmediate() every time the queue becomes empty. With ZipStream, _onEntryFinish() is called synchronously for each addEntry(), so multiple back-to-back entries will enqueue multiple immediates and can call _finalize() more than once (e.g., multiple zipfile.end() calls), which can throw or cause inconsistent stream termination. Coalesce the finalization scheduling so only one pending finalize runs per idle period. setImmediate(() => {
if (this._processing || this._waitingEntries.length > 0) return;
this._finalize();
});
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
lib/tar/stream.js:138
setImmediate-based finalize defers closing correctly, but _onEntryFinish() will schedule a new setImmediate each time the queue becomes empty. With multiple back-to-back entries, this can result in _finalize() being called multiple times (one per scheduled callback), which may throw or behave unexpectedly (e.g., yazl.ZipFile#end()/tar-stream#finalize() are not guaranteed to be idempotent). Consider de-duplicating the scheduled finalize and guarding against double-finalization. setImmediate(() => {
if (this._processing || this._waitingEntries.length > 0) return;
this._finalize();
});
Sorry, something went wrong.
There was an error while loading. Please reload this page.
The directory placeholder emitted 'end' from a setImmediate, so the event fired whether or not the consumer had finished with the entry. A listener that creates the directory asynchronously would therefore be handed the next entry, a file inside that directory, before the directory existed, and the write failed with ENOENT. That is the intermittent "ENOENT ... /xxx/bar.txt" seen in test/zip/uncompress_stream.test.js on loaded CI runners. Reproduces every time by delaying the mkdir in the entry handler. Push EOF instead, so 'end' arrives only once the consumer reads or resumes the entry and the ordering no longer depends on timing.
There was an error while loading. Please reload this page.
[skip ci] ## <small>2.1.3 (2026-08-05)</small> * fix: replace @eggjs/yauzl with upstream yauzl 3.4.0, update yazl to 3.3.1 (#145) ([572a0ba](572a0ba)), closes [#145](#145) [#140](#140) * chore(deps): update dependency @types/node to v24 (#123) ([3499eb2](3499eb2)), closes [#123](#123) * chore(deps): update dependency iconv-lite to ^0.7.0 (#122) ([e7ba2a5](e7ba2a5)), closes [#122](#122)
|
🎉 This PR is included in version 2.1.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
lib/tar/stream.js:138
_onEntryFinish() schedules a setImmediate() finalization every time the queue is empty. When entries are added back-to-back in the same tick (especially for ZipStream, where _onEntryFinish() is synchronous), this can queue multiple finalization callbacks and call _finalize() more than once. Debounce the scheduling so at most one finalize attempt is pending at a time. setImmediate(() => {
if (this._processing || this._waitingEntries.length > 0) return;
this._finalize();
});
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.