npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

s3-multipart-planner

v1.0.2

Published

Plan valid Amazon S3 multipart upload sizes, byte ranges, concurrency waves, and buffer usage without uploading anything.

Readme

s3-multipart-planner

CI npm license

Plan valid Amazon S3 multipart upload chunks before you allocate buffers, create presigned URLs, or send a request. It has zero runtime dependencies and does not need AWS credentials.

This package answers deterministic questions:

  • How large must each part be to stay at or below 10,000 parts?
  • Which byte range belongs to each part?
  • How many concurrency waves are planned?
  • What is the upper-bound payload buffered if every worker holds one full part?

It deliberately does not predict upload speed. Network latency, bandwidth, HTTP connection reuse, retries, server-side throttling, checksums, and client implementation usually matter as much as—or more than—chunk size.

Install

npm install s3-multipart-planner

Requires Node.js 18 or later. The package ships ESM, CommonJS, and TypeScript declarations and can also be bundled for browsers.

Quick start

import { planMultipartUpload } from 's3-multipart-planner';

const MiB = 1024 * 1024;
const plan = planMultipartUpload(260 * MiB, {
  preferredPartSize: 13 * MiB,
  concurrency: 4,
});

console.log(plan.partCount);        // 20 planned UploadPart calls
console.log(plan.waves);            // 5 idealized concurrency waves
console.log(plan.peakBufferedBytes); // 54,525,952 (52 MiB)
console.log(plan.parts[0]);
// {
//   partNumber: 1,
//   start: 0,
//   endExclusive: 13631488,
//   size: 13631488,
//   contentRange: 'bytes 0-13631487/272629760'
// }

CommonJS works too:

const { planMultipartUpload } = require('s3-multipart-planner');

API

planMultipartUpload(objectSize, options?)

objectSize is a positive, safe-integer byte count. All legal S3 multipart sizes are below JavaScript's safe-integer limit, so bigint is not accepted. For a zero-byte object, use S3 PutObject; there is no useful multipart plan.

Options:

| Option | Default | Meaning | | --- | ---: | --- | | preferredPartSize | 8 MiB | Desired bytes per part; must be 5 MiB–5 GiB | | concurrency | 4 | Maximum number of parts processed at once | | alignment | 1 MiB | Round the selected part size up to this byte multiple |

The planner increases preferredPartSize only when necessary to keep the plan within 10,000 parts, then rounds up to alignment. Inspect partSizeAdjusted and the final partSize rather than assuming the preference was used verbatim. Invalid preferences fail early instead of being silently clamped.

The returned object contains:

| Field | Meaning | | --- | --- | | partSize | Selected size of every non-final part | | partCount | Planned payload-call count, excluding retries and control-plane calls | | lastPartSize | Final part size; S3 allows it to be below 5 MiB | | parts | Frozen array of 1-based part numbers and half-open byte ranges | | waves | ceil(partCount / concurrency); a scheduling count, not a time estimate | | activeConcurrency | Workers that can be active given the number of parts | | peakBufferedBytes | Upper bound if each active worker buffers its entire payload | | minimumLegalPartSize | Smallest aligned size that satisfies S3's part-count limit |

contentRange is included for systems that use HTTP byte ranges. The native S3 UploadPart API takes a body and part number; it does not require this header.

S3_LIMITS

Exports the constraints used by the planner: 5 MiB minimum part size, 5 GiB maximum part size, 10,000 parts, and a computed maximum of 53,687,091,200,000 bytes (5 GiB × 10,000). The last part is exempt from the minimum.

Errors

Validation failures throw MultipartPlanError, a RangeError with a stable code, such as INVALID_OBJECT_SIZE, INVALID_PART_SIZE, OBJECT_TOO_LARGE, or ALIGNMENT_EXCEEDS_LIMIT.

Using a plan

The package is SDK-agnostic. A typical uploader reads each half-open range [start, endExclusive), sends it with partNumber, records the returned ETag, then completes the multipart upload in ascending part order.

partCount counts planned UploadPart payload requests only. The baseline multipart sequence is therefore partCount + 2 network calls: create, parts, and complete. A real upload may also make abort, checksum, retry, redirect, discovery, or credential-refresh requests. peakBufferedBytes is min(objectSize, activeConcurrency × partSize) and assumes each active worker holds one complete part; streaming SDKs may buffer less, while transforms, checksums, retries, and SDK queues may buffer more. For example, AWS SDK upload helpers have their own queue and buffering behavior.

Limits and non-goals

  • This plans ranges; it does not read files, upload data, sign requests, or pick the fastest chunk size.
  • The current S3 documentation describes a 48.8 TiB maximum implied by 10,000 × 5 GiB parts. Some SDK releases, S3-compatible providers, and older tooling still document or enforce a historical 5 TiB object limit. Verify the client and service you actually deploy.
  • Memory and timing fields are planning estimates, not runtime measurements.
  • Ranges use an inclusive start and exclusive endExclusive. S3 copy-range APIs commonly use an inclusive end, so convert deliberately.
  • parts contains up to 10,000 entries. That is intentional for easy range and presigned-URL generation.
  • JavaScript number is used instead of bigint; the package accepts integers only and verifies Number.isSafeInteger.

Why this exists

An open issue on a public parallel-upload example asks why changing chunk sizes did not materially change a 260 MB upload time. Existing tools found during the initial research—such as @aws-sdk/lib-storage, s3, and s3-upload-stream—perform uploads and may adjust part sizes internally. Uppy also exposes multipart chunk sizing. This package has a narrower role: make the legal chunk plan, exact ranges, and resource tradeoffs inspectable before an uploader runs. It does not claim that multipart sizing is a novel concept.

The constraints come from AWS's S3 multipart upload limits. AWS's multipart overview documents the create/upload/complete lifecycle. AWS's JavaScript SDK migration guide points upload users to @aws-sdk/lib-storage.

Troubleshooting

INVALID_PART_SIZE — Pass a whole-byte value from 5 MiB through 5 GiB. Remember that MiB is 1024 * 1024, not one million bytes.

partSizeAdjusted is true — The preferred size would create more than 10,000 parts or was rounded to the requested alignment. Use plan.partSize for the actual reads.

Actual memory is higher than peakBufferedBytes — Inspect your SDK queue, stream high-water marks, retry buffers, transforms, and checksum implementation.

Changing the part size did not speed up the upload — Compare concurrency, bandwidth, latency, connection reuse, CPU/checksum cost, and server throttling. The plan describes work; it does not benchmark the path.

Runnable example and related tool

Run examples/plan-upload.mjs for a deterministic 260 MiB plan that checks part boundaries, concurrency waves, and buffer usage. The example ships in the npm tarball.

For validating HTTP byte-range resume responses rather than planning S3 upload parts, see resume-range-audit.

Development

npm test
npm run lint
npm run check

See CONTRIBUTING.md, SECURITY.md, and the changelog. Released under the MIT License.