v0.17.2
Critical bug-fix release. Eleven concurrency / DOS / data-integrity bugs surfaced by a deep code audit, plus refreshed security and example documentation. No public API changes — drop-in over v0.17.1.
Fixed
FilesystemTrackerlock fall-through (race condition). Three branches (fopenfailure,flockfailure, cloud disk withoutpath()) silently ran the critical section without a lock — every chunk write / status flip was a lost-update race. The branches now throwChunkyExceptionwith actionable error messages, so the operator gets a fail-fast signal instead of corrupted metadata.ChunkyManager::withBatchLock()lock fall-through (race condition). The same silent fall-through pattern existed for batch counter updates —flockunavailable on cloud disks would silently bypass the lock. Now throws with guidance to usechunky.lock_driver = "cache"for cloud disks.BatchUploader.uploaders[]memory leak. Every per-fileChunkUploaderwas pushed and never removed, retaining 100MB+Filereferences plus closure state for the lifetime of the BatchUploader. A 10000-file enqueue session held all 10000 uploader instances in memory. The per-file uploader is now spliced out of the array anddestroy()-ed in thefinallyblock.ChunkUploader.cancel()did not resetlastFile/lastMetadata. Acancel()followed byretry()silently re-uploaded the cancelled file (the retry path detected the still-setlastFile). Cancel now drops the resume state too.- Sticky-replay race (
ChunkUploader.on()andBatchUploader.on()). ThequeueMicrotaskre-delivery of a cachedcomplete/errorto a late subscriber did not check whether the subscriber had synchronously unsubscribed before the microtask drained. The typical ReactuseEffectcleanup-then-resubscribe pattern triggered the callback afterunsub()returned. The microtask now confirms the listener is still in the Set before delivering. CompletionWatcher.extendTimeoutOnProgressMsno-op whentimeoutMs = 0. The progress-extension safeguard only ran when an existingtimeoutTimerwas present, so opting into "extend on progress" with no static deadline silently never started the safeguard. The timer now (re)creates on every progress tick regardless of whether a static deadline was set.
Added
max_chunks_per_uploadconfig cap (default 100k). A pathological initiate (file_size = 1TB, chunk_size = 1KB → 1 billion chunks) would explode the tracker row and grind the UI to a halt. The newfile_sizevalidation rule rejects any size that would require more chunks than the configured cap, with a helpful error message pointing atchunk_size/ the cap setting.throttle:chunkyrate limiter. The package now registers aRateLimiter::for('chunky')keyed by user id (or IP for anonymous traffic) and addsthrottle:chunkyto the default route middleware. Configurable viachunky.throttle.attempts/chunky.throttle.decay_minutes(default 120/min) — set attempts to 0 to disable. Without this, the public batch/upload status endpoints could be hammered with random UUIDs to drive S3 GET costs.- Versioned cache-key prefix. Every lock / idempotency / counter cache key now goes through
Support\CacheKeyswith a configurablechunky.cache.prefix(defaultchunky:v1:). Lets a future major release invalidate cached payloads cleanly without cooperating cache backends — and removes the collision risk with other packages using a barechunky:prefix. - Disk-space pre-flight + assembled-size assertion in
DefaultChunkHandler. Theassemble()method now accepts an optional?int $expectedSize, and when supplied (a) refuses to start an assembly the staging volume can't hold (10% margin) and (b) verifies the assembled temp file matches the expected size before publishing, so silent chunk-corruption truncations can never reach the final disk. TheAssembleFileJobpasses$metadata->fileSizeso the checks are active by default. Cache::locktry/finallyrelease inFilesystemTracker::withCacheLock()andChunkyManager::withBatchLock(). Previously a callback exception left the lock held until its TTL expired — a mild DoS amplifier under churn.
Changed (CI / repo tooling — no published artefact change)
- Dependabot auto-merge for patch and minor updates. New
.github/workflows/dependabot-auto-merge.ymlwatches Dependabot PRs and, after the regular CI matrix passes, approves and enables auto-merge (squash) forversion-update:semver-patchandversion-update:semver-minorupdates. Major bumps get a comment instead and stay open for manual review. The repository's "Allow auto-merge" and "Automatically delete head branches" settings are required and have been enabled. dependabot.ymlper-package grouping. Eachpackages/{core,vue3,react,alpine}/config now defines a single*-dev-dependenciesgroup coveringtypescript,esbuild,@types/*, and the framework peer (vue/react). Last week's update produced 8 separate PRs for typescript+esbuild across the 4 packages; the same updates would now land as 4 grouped PRs (one per package). Thegithub-actionsecosystem also got anactionsgroup so all action bumps land in a single PR.
Documentation
SECURITY.mdSupported Versions table updated to0.17.x(was stuck at0.14.x); Hardening Surface section now lists the rate limiter, lock-driver compatibility guard, and cache-key namespacing.examples/en/configuration.mdrewritten as a recipe-focused companion to the canonicalconfig/chunky.phpreference. The previous version was stuck at v0.7-era keys.
npm packages
- All packages bumped to
0.17.2(frontend bug fixes — BatchUploader memory leak, sticky-replay race, ChunkUploader cancel, CompletionWatcher).