Dashboard › cli › Distillation
1e64a763-91dd-4561-b937-7f51cbd2333b["lore_tm_v1_rjsbwCAYDhMYNRLAvwt6vvUFDs22auGyj6u1nwQi58w","lore_tm_v1_IBj59APxpJ8Py5PqzrduMV_Q1YFxx2200ISzGR1RgQ4","lore_tm_v1_l9v0R8tKOCV7mxhA1WS5EsQ8v7R2hGJJGWFC2d5Jf_o","lore_tm_v1_Z93vRwP5GulkUAi9_Rr37MIVSQwzRROjyW97LX26FOU","lore_tm_v1_Ch4Zfa9401k6rvPUMGvq3kSFxsC-qAAqOjTSGqgHwos","lore_tm_v1_yqstw3KhVOKd77fVwZw5Z6JhooUEKBf4GsBzfjWetb8"]
Date: Sep 10, 2026
Content-Type must never be lost to a raw application/octet-stream or a missing header.packages/cli/test/lib/sentry-client.test.ts imports __injectTimeoutOverrideForTests, __resolveRequestTimeoutMsForTests, getSdkConfig, and resetAuthenticatedFetch from ../../src/lib/sentry-client.js; test setup stores globalThis.fetch, sets non-expiring token "test-token" via setAuthToken("test-token"), calls resetAuthenticatedFetch(), restores fetch afterward, and obtains authenticated fetch through getSdkConfig("https://us.sentry.io").fetch.sentry-client.test.ts regression retries a POST with a string body without re-consuming the body sends POST JSON {"slug":"acme"} with Content-Type: application/json; mocked first response is 503 and second is 200 JSON; asserts 2 calls and both observed bodies equal the original JSON.sentry-client.test.ts regression retries a POST built from a Request object without consuming its body sends a sole-argument Request to https://us.sentry.io/api/0/__test_request_body__/autofix/, with POST JSON payload {"stopping_point":"root_cause"}; mock clones and reads each Request, returns 502 then 200, and asserts 2 calls with the identical payload both times.sentry-client.test.ts existing trusted-redirect test follows a trusted 307 redirect without consuming a Request body POSTs {"redirect":"preserve-body"} from https://us.sentry.io/api/0/__test_redirect_request_body__/ to same-origin destination /redirected/; mock asserts Request input, returns 307 then 200, and expects both requests to carry the same payload and Authorization: Bearer test-token.sentry-client.test.ts regression retries with a ReadableStream body by materializing once creates a ReadableStream containing "streamed-body" and calls POST with duplex: "half" (required by Node fetch for streamed bodies; Bun accepts it as a no-op). Mock returns 500 then 200; asserts 2 calls and "streamed-body" observed on each attempt.sentry-client.test.ts FormData regression retries a FormData body without losing the multipart boundary creates FormData containing file: a Blob of bytes [1, 2, 3, 4], MIME type "application/octet-stream", filename "chunk.bin", then POSTs to /api/0/__test_formdata_body__/chunks/. Mock builds new Request(input as string, init), reads headers/body, returns 503 then 200; asserts each attempt has Content-Type matching /^multipart\/form-data; boundary=.+/u and body containing name="file" and "chunk.bin". It explicitly allows Bun to generate fresh boundaries per serialization, requiring each header/body pair to remain internally consistent.sentry-client.test.ts begins timeout classification test surfaces TimeoutError on the last attempt when our own timeout fires, injecting override { pattern: new RegExp('/___timeout-test___/'), timeoutMs: 50 } to avoid the normal 30-second timeout.packages/cli/src/lib/oauth.ts implements RFC 8628 Device Authorization Grant; getSentryUrl() lazily resolves getConfiguredSentryUrl() ?? DEFAULT_SENTRY_URL so URL arguments or SENTRY_URL set after import affect device flow and refresh.oauth.ts resolves OAuth client ID at call time in priority order: SENTRY_CLIENT_ID environment variable, SENTRY_CLIENT_ID_BUILD build-time define, then committed DEFAULT_OAUTH_CLIENT_ID; self-hosted instances must override via SENTRY_CLIENT_ID or SENTRY_CLIENT_ID_BUILD.oauth.ts exports ordered OAUTH_SCOPES: 1. "project:read", 2. "project:write", 3. "project:admin", 4. "org:read", 5. "event:read", 6. "event:write", 7. "member:read", 8. "team:read", 9. "team:write", 10. "alerts:read", 11. "alerts:write"; SCOPES is their space-joined string.packages/cli/src/lib/custom-headers.ts parses SENTRY_CUSTOM_HEADERS or SQLite defaults.headers for self-hosted Sentry reverse-proxy authentication (for example Google IAP or Cloudflare Access). Header format is semicolon- or newline-separated Name: Value pairs; source precedence is nonblank SENTRY_CUSTOM_HEADERS over nonblank SQLite defaults.custom-headers.ts forbids caller override of authorization, host, content-type, content-length, user-agent, sentry-trace, and baggage; invalid/missing names, invalid RFC 7230-token field names, and forbidden names throw ConfigError. Parsed header tuples preserve declaration order and are cached by raw source.custom-headers.ts applies headers only to non-SaaS configured instances and only when isRequestOriginTrustedForCustomHeaders(requestUrl) accepts the target. With no token, explicit matching login anchor is required; otherwise it fails closed. It warns once and skips configured custom headers on SaaS or an untrusted destination, preventing IAP tokens/mTLS headers from leaking to unauthenticated hosts.packages/cli/src/lib/db/auth.ts:634-705 refreshToken(options: RefreshTokenOptions = {}) gives a nonblank SENTRY_FORCE_ENV_TOKEN priority: if getEnvToken() exists, returns it without refresh. Otherwise stored auth is used; absent stored token falls back to env token, then throws AuthError("not_authenticated").refreshToken() returns a non-expired stored token when remaining lifetime ratio exceeds REFRESH_THRESHOLD; on absent refresh token it calls clearAuth(), falls back to env token if available, otherwise throws AuthError("expired", "Session expired and no refresh token available. Run 'sentry auth login'."). Concurrent refreshes share refreshPromise, which is cleared in finally; refresh failures clear auth only for AuthError, not network errors.HostScopeError class declaration in packages/cli/src/lib/errors.ts:139.