Drop 6 prod deps to shrink Electron installers (~6 MB)#576
Open
IrosTheBeggar wants to merge 1 commit intomasterfrom
Open
Drop 6 prod deps to shrink Electron installers (~6 MB)#576IrosTheBeggar wants to merge 1 commit intomasterfrom
IrosTheBeggar wants to merge 1 commit intomasterfrom
Conversation
Replace runtime dependencies whose features mStream barely exercises with
small, self-contained equivalents. With asar=false, every node_modules file
ships unpacked into the installer, so a smaller dependency graph translates
directly to faster downloads and extracts.
Removed:
- archiver → yazl (~4.2 MB transitive: archiver-utils, tar-stream,
zip-stream, compress-commons, lazystream, readdir-glob,
glob, async). New helper at src/util/zip-stream.js
centralizes the create-zip-and-pipe-to-response pattern
used by 4 endpoints; recursive directory walks use
fs.readdir({ recursive: true, withFileTypes: true }).
- undici → node:https.request (~1.5 MB). Single use was a
TLS-cert-ignoring POST to Syncthing's REST API.
- m3u8-parser → simple line parser (~518 KB). Used only to extract URIs
from .m3u music playlists; the library is built for HLS
streaming manifests we never produce. Replacement also
fixes a latent bug where the previous fallback path
returned `#EXTM3U` as a "song".
- mime-types → static image-MIME map at src/util/image-mime.js. Three
narrow call sites (album art extension lookup); the full
mime-db isn't needed.
- nanoid → crypto.randomBytes via src/util/ids.js. Six call sites,
all using the default alphabet.
- cookie-parser → inline middleware in src/server.js. Hardened with
Object.create(null), idempotency guard, RFC 6265 quote
stripping, and `%`-presence check before
decodeURIComponent. Behavior locked in by 24 parity
tests at test/cookie-middleware-parity.test.mjs
validated against cookie-parser 1.4.7's audited output.
Side-effect bug fixes uncovered during the audit:
- src/util/m3u.js fallback no longer returns #EXTM3U as a track URI.
- src/db/scanner.mjs no longer produces filenames like "<hash>.false" when
music-metadata returns an unrecognized picture format (now falls back to
".jpg" like ytdl.js already did).
- src/db/image-compress-script.js no longer throws "Cannot read property
'startsWith' of false" on unknown extensions.
Verification:
- npm test: 450/450 pass (24 new cookie parity tests; baseline 426 unchanged).
- npm run lint: 82 problems before / 82 after — no new issues.
- Live server smoke test: logged in via header AND cookie auth, exercised
all four ZIP-producing endpoints (download/m3u, download/directory,
download/zip, admin/logs/download) including a recursive directory walk
and a UTF-8 filename round-trip; every ZIP passes integrity check.
Kept:
- tree-kill — handles cross-platform process-tree termination
(taskkill /T /F on Windows, recursive pgrep/ps on Unix); 80 lines of
re-implementation for 8 KB savings is bad ROI.
- command-exists — savings (13 KB) too small to justify replacement.
- joi, winston, electron-updater — kept per discussion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace 6 runtime dependencies with small, self-contained equivalents. Because
package.jsonbuilds the Electron app withasar: false, every file innode_modulesships unpacked into the installer — a smaller dependency graph translates directly to faster downloads and extracts. Net savings: ~6 MB off the production node_modules tree.What changed
archiveryazl+ new helper atsrc/util/zip-stream.jsundicinode:https.requestm3u8-parsermime-typessrc/util/image-mime.jsnanoidcrypto.randomByteshelper atsrc/util/ids.jscookie-parsersrc/server.jsx-access-tokencookie)What was kept (and why)
tree-kill— handles cross-platform process-tree termination:taskkill /T /Fon Windows, recursivepgrep/pson Unix. Re-implementing this correctly is ~80 lines for 8 KB savings.command-exists— handles input sanitization + file-path-vs-PATH-name detection. Savings (13 KB) too small to justify any replacement.joi,winston,electron-updater— discussed; kept.Side-effect bug fixes uncovered during the audit
src/util/m3u.js: the existing fallback path returned#EXTM3Uas a track URI when the parser yielded zero segments. Inline parser filters#-prefixed lines correctly.src/db/scanner.mjs: used to produce filenames like<hash>.falsewhenmusic-metadatareturned an unrecognized picture format (mime.extension(...)returns booleanfalse). Now falls back to.jpglikesrc/api/ytdl.jsalready did.src/db/image-compress-script.js: existingmime.lookup(ext).startsWith('image')could throwCannot read property 'startsWith' of falseon unknown extensions. New helper returns a clean boolean.Cookie-parser swap: hardening + parity tests
After the initial inline-middleware swap, the audit against
cookie-parser1.4.7's source surfaced four small differences worth closing. The middleware now uses:Object.create(null)forreq.cookies(no prototype-pollution surprises if a cookie name collides withObject.prototypekeys liketoString)if (!req.cookies)) — matches cookie-parser when middleware is mounted twice"value"→value)%-presence check beforedecodeURIComponent(cheap perf parity)Behavior is locked in by 24 parity tests at
test/cookie-middleware-parity.test.mjscovering empty headers, JWT-with-==-padding, multi-cookie headers, whitespace+tabs, leading/trailing semicolons, duplicate keys (first wins), quoted values, percent-encoding, malformed percent-encoding, no-equals, equals-in-value, and Object.prototype-collision names. Each expected value is validated against cookie-parser 1.4.7's audited output.Verification
npm test: 450/450 pass (24 new cookie parity tests; baseline 426 unchanged for Subsonic / DLNA / scanner / lyrics / waveform suites)npm run lint: 82 problems before / 82 after — zero new lint issuesx-access-tokenheader and via cookie — both workPOST /api/v1/download/m3u→ 23 KB ZIP, 3 mp3s + the m3u file ✅POST /api/v1/download/directory(flat) → 22 KB, 3 files ✅POST /api/v1/download/directory(recursive, 2 subdirs) → 30 KB, 4 files preserving structure ✅POST /api/v1/download/zip(file array) → 22 KB ✅POST /api/v1/download/zipwith UTF-8 filenamecafé_日本語.mp3→ round-trips correctly ✅GET /api/v1/admin/logs/download→ valid ZIP ✅zipfile.testzip()integrity checkTest plan
npm installproduces a smallernode_modules(archiver,undici,m3u8-parser,nanoid,cookie-parsershould be gone from prod tree;mime-typesstays as Express transitive).m3uplaylist, a multi-album zip, andadmin/logs/downloadhttps.requestswap is the only Syncthing-touching change)nanoid→crypto.randomBytes('base64url')swap)🤖 Generated with Claude Code