feat(cli-core): migrate HTTP request layer to get-it v9#1534
Conversation
Replace the get-it v8 middleware stack in @sanity/cli-core/request with the fetch-based v9 createRequester API, and update CLI consumers for the new response shape, streaming, and redirect options.
📦 Bundle Stats —
|
| Metric | Value | vs main (643f3d4) |
|---|---|---|
| Internal (raw) | 2.2 KB | - |
| Internal (gzip) | 838 B | - |
| Bundled (raw) | 11.20 MB | - |
| Bundled (gzip) | 2.11 MB | - |
| Import time | 682ms | -2ms, -0.3% |
bin:sanity
| Metric | Value | vs main (643f3d4) |
|---|---|---|
| Internal (raw) | 782 B | - |
| Internal (gzip) | 423 B | - |
| Bundled (raw) | 9.90 MB | - |
| Bundled (gzip) | 1.78 MB | - |
| Import time | 1.64s | +13ms, +0.8% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — @sanity/cli-core
Compared against main (643f3d47)
| Metric | Value | vs main (643f3d4) |
|---|---|---|
| Internal (raw) | 114.1 KB | - |
| Internal (gzip) | 29.2 KB | - |
| Bundled (raw) | 21.76 MB | - |
| Bundled (gzip) | 3.46 MB | - |
| Import time | 599ms | -8ms, -1.3% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — @sanity/cli-build
Compared against main (643f3d47)
@sanity/cli-build/_internal/build
| Metric | Value | vs main (643f3d4) |
|---|---|---|
| Internal (raw) | 113.1 KB | -94 B, -0.1% |
| Internal (gzip) | 28.6 KB | -33 B, -0.1% |
| Bundled (raw) | 17.61 MB | -140.6 KB, -0.8% |
| Bundled (gzip) | 3.52 MB | -39.0 KB, -1.1% |
| Import time | 888ms | +7ms, +0.8% |
@sanity/cli-build/_internal/env
| Metric | Value | vs main (643f3d4) |
|---|---|---|
| Internal (raw) | 1.8 KB | - |
| Internal (gzip) | 644 B | - |
| Bundled (raw) | 1.31 MB | - |
| Bundled (gzip) | 333.8 KB | - |
| Import time | 100ms | +1ms, +1.3% |
@sanity/cli-build/_internal/extract
| Metric | Value | vs main (643f3d4) |
|---|---|---|
| Internal (raw) | 8.6 KB | - |
| Internal (gzip) | 2.7 KB | - |
| Bundled (raw) | 155.0 KB | - |
| Bundled (gzip) | 39.5 KB | - |
| Import time | 189ms | +2ms, +1.2% |
🗺️ ./_internal/env · ./_internal/extract · @sanity/cli-build:./_internal/build treemap too large to embed · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — create-sanity
Compared against main (643f3d47)
| Metric | Value | vs main (643f3d4) |
|---|---|---|
| Internal (raw) | 908 B | - |
| Internal (gzip) | 483 B | - |
| Bundled (raw) | 931 B | - |
| Bundled (gzip) | 491 B | - |
| Import time | ❌ ChildProcess denied: node | - |
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Force global fetch in backup download tests so nock can intercept undici, and mock nodeReadableFromWeb in dataset import tests.
Coverage Delta
Comparing 10 changed files against main @ Overall Coverage
|
get-it v9's Node fetch uses undici with a private dispatcher, so nock cannot intercept createRequester traffic. Force globalThis.fetch in the shared Vitest setup so GraphQL deploy and other nock-based suites work.
The dependency bump and request-layer migration should not force major versions; majors were only inferred from the breaking bang in the PR title.
Cancel web response bodies when Node streams end early. Restore separate response and idle read timeouts for backup downloads.
Cancel the reader directly when a Node stream is destroyed so pending reads cannot block cleanup.
Destroy destination streams when requests fail before body streaming begins.
Give each Node fetch attempt its own response timeout. Guard pending read teardown from cancellation races.
Apply the response deadline as inner middleware. Retries get fresh timers while tests can inject global fetch.
…-v9-28de # Conflicts: # packages/@sanity/cli/src/commands/datasets/import.ts
rexxars
left a comment
There was a problem hiding this comment.
Overall this looks good, just left some comments and potential room for improvemnts
Detect User-Agent overrides case-insensitively. Derive CreateRequesterOptions from get-it RequesterOptions. Re-export the real get-it HttpError from the cli-test request mock. Defer backup download destinations until a response arrives. Disable the request timeout for streamed dataset imports.
There was a problem hiding this comment.
Pull request overview
This PR upgrades the CLI monorepo’s HTTP layer by migrating @sanity/cli-core/request from get-it v8 to get-it v9 (fetch-based), updating downstream call sites to the v9 response shape/streaming API, and introducing shared helpers for Web↔Node stream interop and more robust backup streaming.
Changes:
- Migrate
@sanity/cli-core/requestto get-it v9 (createRequesteroptions shape, exported types/helpers, and newnodeReadableFromWeb()). - Update CLI/CLI-build consumers for v9 semantics (
as: 'stream',redirect: 'manual',Headersaccess, etc.). - Refactor backup downloads to a shared
downloadStream()with per-attempt connection timeout + rolling read timeout, plus new tests.
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Updates lockfile for get-it v9 and related dependency graph changes. |
| packages/@sanity/eslint-config-cli/eslint.config.mjs | Allows ReadableStream usage for Node engines required by get-it v9. |
| packages/@sanity/cli/test/setup.ts | Mocks createRequester in tests to use globalThis.fetch for HTTP interception. |
| packages/@sanity/cli/src/services/graphql.ts | Updates error handling to use HttpError from the new request layer. |
| packages/@sanity/cli/src/services/getUrlHeaders.ts | Migrates HEAD request handling to v9 redirect/Headers behavior. |
| packages/@sanity/cli/src/commands/datasets/import.ts | Updates dataset import source streaming to v9 as: 'stream' + Web→Node conversion. |
| packages/@sanity/cli/src/commands/datasets/tests/import.test.ts | Adjusts request mocks for the new stream conversion helper. |
| packages/@sanity/cli/src/actions/backup/downloadStream.ts | Adds shared backup streaming helper with retry + timeouts + deferred destination creation. |
| packages/@sanity/cli/src/actions/backup/downloadStream.test.ts | Adds unit coverage for streaming, timeouts, retries, and cancellation behavior. |
| packages/@sanity/cli/src/actions/backup/downloadDocument.ts | Refactors document download to use downloadStream() and returns string content. |
| packages/@sanity/cli/src/actions/backup/downloadAsset.ts | Refactors asset download to use downloadStream() and simplify piping logic. |
| packages/@sanity/cli-test/src/mocks/cli-core/request.ts | Updates request mocks for v9 exports and preserves HttpError instanceof behavior. |
| packages/@sanity/cli-test/package.json | Adds get-it v9 as a dependency to support the mock’s re-export. |
| packages/@sanity/cli-core/src/request/nodeReadableFromWeb.ts | Introduces Web ReadableStream → Node Readable adapter for v9 streaming responses. |
| packages/@sanity/cli-core/src/request/createRequester.ts | Reworks requester creation around get-it v9 options + default headers/debug behavior. |
| packages/@sanity/cli-core/src/request/tests/nodeReadableFromWeb.test.ts | Adds tests for stream conversion and cancellation/error propagation. |
| packages/@sanity/cli-core/src/request/tests/createRequester.test.ts | Updates tests for v9 createRequester behavior, passthrough options, and header logic. |
| packages/@sanity/cli-core/src/_exports/request.ts | Updates the public export surface to v9 types/helpers and middleware still available. |
| packages/@sanity/cli-core/README.md | Updates documented exports to match the new request API surface. |
| packages/@sanity/cli-core/package.json | Bumps get-it dependency to ^9.1.0. |
| packages/@sanity/cli-build/src/util/compareDependencyVersions.ts | Updates HEAD request logic for v9 response shape and redirect handling. |
| packages/@sanity/cli-build/src/util/tests/compareDependencyVersions.test.ts | Updates mocks to match v9 Headers + status response shape. |
| .changeset/pr-1534.md | Adds changeset entries for the migration across affected packages. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Replace the hand-rolled requester mock with createMockFetch and vitest matchers. Keep custom fetch stubs for stalled streaming bodies and cancellation spies.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1ff4e7b. Configure here.
…-v9-28de # Conflicts: # pnpm-lock.yaml
Adopt structured {headers, total} timeouts for backup downloads.
Drive download stream tests fully through get-it/mock streamBody().
Migrate the new api service off removed v8 request options.
rexxars
left a comment
There was a problem hiding this comment.
Sweet. Thanks for also help prove the mocks/api gaps in get-it v9!

Description
Upgrade
@sanity/cli-corefrom get-it v8 to v9 (now 9.3.0) and migrate the request layer to the new fetch-based API.@sanity/cli-core/requestnow uses get-it v9-style options (headers,httpErrors,debug,middleware, structuredtimeout, etc.) oncreateRequester, and re-exports the v9 helpers used by CLI consumers (HttpError,TimeoutError, response types,retry/debug,nodeReadableFromWeb()).Call sites in
@sanity/cliand@sanity/cli-buildwere updated for the v9 response shape (status/Headers),as: 'stream' | 'text', andredirect: 'manual'— including the newsanity apiservice that landed onmainagainst the v8 API.What to review
packages/@sanity/cli-core/src/request/createRequester.ts—CreateRequesterOptionsderives from get-it'sRequesterOptions; User-Agent overrides detected case-insensitivelytimeout: {headers: 15s, total: false}(fresh per-retry headers deadline, no custom middleware) plus the 3-minute idle-read timer; destinations are deferred until a response arrivesservices/api.tsmigrated to v9:httpErrors: false+as: 'text',Headersaccess,status/statusText, andURLSearchParamsto preserve repeated query paramstimeout: false);getUrlHeaders/ GraphQL and dependency version comparison unchanged from earlier revisions@sanity/cli-core/request; cli-test request mock re-exports get-it's realHttpError/TimeoutErrorglobalThis.fetchforcreateRequesterso nock can intercept (get-it v9 Node entry uses undici with a private dispatcher)downloadStreamtests run entirely onget-it/mock:streamBody()/streamStall()for stalled bodies,toHaveBeenCancelled()for cancellation,respondPersist({delay})+ fake timers for per-attempt deadlinesget-itadded tominimumReleaseAgeExclude(first-party, matching the existing@sanity/*pattern)Testing
createRequester(incl. case-insensitive User-Agent and get-it option passthrough),nodeReadableFromWeb, backup stream timeout/cancellation/deferred-destination behavior, and the nock-basedsanity apicommand suitepnpm check:types,pnpm check:lint,pnpm check:deps, andpnpm build:clipassNotes for release
N/A