Skip to content

[wrangler] Reduce Pipelines S3 bundle size - #15414

Draft
james-elicx wants to merge 1 commit into
mainfrom
codex/pipelines-narrow-sigv4
Draft

james-elicx wants to merge 1 commit into
mainfrom
codex/pipelines-narrow-sigv4

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Fixes no linked issue.

Wrangler's Pipelines credential validation only uses S3 HeadBucket and ListObjectsV2 with MaxKeys: 1, but importing S3Client pulls 1,234,988 bytes of AWS and Smithy code into the published CLI bundle, including credential-provider paths that cannot be reached because Pipelines always supplies credentials and an endpoint explicitly.

This replaces that runtime client with a minimal R2 client built on aws4fetch's low-level SigV4 signer and Wrangler's existing undici fetch. It preserves path-style bucket URLs, auto/s3 signing scope, the empty-payload SHA-256 header, non-success failures, manual redirect handling, response-body disposal, and the SDK's default three attempts for network, throttling, and transient server failures. The existing outer propagation retry for newly created service tokens is unchanged.

Bundle measurements

Measurements use equivalent source-map-disabled Wrangler builds from the same checkout.

Size Before After Saving
Raw cli.js 20,561,388 B 19,153,577 B 1,407,811 B / 6.85%
Gzip 3,650,005 B 3,457,556 B 192,449 B / 5.27%

The emitted metafile's AWS/Smithy contribution falls from 1,234,988 bytes across 751 inputs to zero. aws4fetch contributes 10,405 bytes.

Validation

  • pnpm --dir packages/wrangler exec vitest run src/__tests__/pipelines-r2-client.test.ts src/__tests__/pipelines-setup.test.ts — 27 passed
  • pnpm --dir packages/wrangler check:type
  • pnpm --dir packages/wrangler type:tests
  • SOURCEMAPS=false pnpm run build --filter wrangler --force
  • pnpm run check --filter wrangler

  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: This is an internal dependency and credential-validation refactor with no user-facing API or behavior change.

Note

This is a contribution from an AI agent: Codex.

@changeset-bot

changeset-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: ad1e853

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@james-elicx james-elicx added the ci:no-changeset-required Skip pull request checks for a valid changeset label Aug 28, 2026
@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Aug 28, 2026
Comment on lines +84 to +114
try {
const response = await fetch(signedRequest.url, {
method: signedRequest.method,
headers: Object.fromEntries(signedRequest.headers),
redirect: "manual",
});
await response.arrayBuffer();

if (response.ok) {
return;
}

const error = new R2RequestError(response.status, response.statusText);
if (
attempt === MAX_ATTEMPTS ||
!RETRYABLE_STATUS_CODES.has(response.status)
) {
throw error;
}
} catch (error) {
if (
attempt === MAX_ATTEMPTS ||
(error instanceof R2RequestError &&
!RETRYABLE_STATUS_CODES.has(error.status))
) {
throw error;
}
}

const maximumDelay = INITIAL_RETRY_DELAY_MS * 2 ** (attempt - 1);
await setTimeout(Math.random() * maximumDelay);

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.

The AWS SDK retry strategy honors Retry-After, but this replacement retries a throttled request within at most 300 ms. R2/Cloudflare rate-limit responses can direct clients to wait longer, so verifyR2Credentials() can now reject valid credentials instead of performing its prior retry behavior.

Suggested change
try {
const response = await fetch(signedRequest.url, {
method: signedRequest.method,
headers: Object.fromEntries(signedRequest.headers),
redirect: "manual",
});
await response.arrayBuffer();
if (response.ok) {
return;
}
const error = new R2RequestError(response.status, response.statusText);
if (
attempt === MAX_ATTEMPTS ||
!RETRYABLE_STATUS_CODES.has(response.status)
) {
throw error;
}
} catch (error) {
if (
attempt === MAX_ATTEMPTS ||
(error instanceof R2RequestError &&
!RETRYABLE_STATUS_CODES.has(error.status))
) {
throw error;
}
}
const maximumDelay = INITIAL_RETRY_DELAY_MS * 2 ** (attempt - 1);
await setTimeout(Math.random() * maximumDelay);
let retryAfterMs = 0;
try {
const response = await fetch(signedRequest.url, {
method: signedRequest.method,
headers: Object.fromEntries(signedRequest.headers),
redirect: "manual",
});
await response.arrayBuffer();
if (response.ok) {
return;
}
const retryAfter = response.headers.get("retry-after");
if (retryAfter !== null) {
const retryAfterSeconds = Number(retryAfter);
if (Number.isNaN(retryAfterSeconds)) {
const retryAfterDate = new Date(retryAfter).getTime();
if (!Number.isNaN(retryAfterDate)) {
retryAfterMs = Math.max(0, retryAfterDate - Date.now());
}
} else {
retryAfterMs = retryAfterSeconds * 1000;
}
}
const error = new R2RequestError(response.status, response.statusText);
if (
attempt === MAX_ATTEMPTS ||
!RETRYABLE_STATUS_CODES.has(response.status)
) {
throw error;
}
} catch (error) {
if (
attempt === MAX_ATTEMPTS ||
(error instanceof R2RequestError &&
!RETRYABLE_STATUS_CODES.has(error.status))
) {
throw error;
}
}
const maximumDelay = INITIAL_RETRY_DELAY_MS * 2 ** (attempt - 1);
await setTimeout(Math.max(retryAfterMs, Math.random() * maximumDelay));

@ask-bonk

ask-bonk Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

I'm Bonk, and I've done a quick review of your PR.

Replaces Pipelines S3 validation with a lightweight SigV4 R2 client.

  1. Posted 1 actionable inline suggestion.

github run

@pkg-pr-new

pkg-pr-new Bot commented Aug 28, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@15414

@cloudflare/build-output-utils

npm i https://pkg.pr.new/@cloudflare/build-output-utils@15414

@cloudflare/codemods

npm i https://pkg.pr.new/@cloudflare/codemods@15414

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@15414

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@15414

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@15414

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@15414

miniflare

npm i https://pkg.pr.new/miniflare@15414

@cloudflare/pages-functions

npm i https://pkg.pr.new/@cloudflare/pages-functions@15414

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@15414

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@15414

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@15414

@cloudflare/vitest-plugin

npm i https://pkg.pr.new/@cloudflare/vitest-plugin@15414

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@15414

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@15414

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@15414

wrangler

npm i https://pkg.pr.new/wrangler@15414

commit: ad1e853

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

Labels

ci:no-changeset-required Skip pull request checks for a valid changeset

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants