Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | 13x 13x 9x 9x 9x 1x 8x 18x 14x 4x 4x 2x 2x 7x 7x 6x 1x 1x 1x 1x 1x 1x 1x 5x 5x 5x 2x 2x 2x 2x 2x 2x | /**
* DSN-based envelope transport for Sentry's event ingestion pipeline.
*
* Unlike the Web API (which uses Bearer token auth), envelope ingestion
* authenticates via the DSN's public key embedded in the request URL.
* This is the same mechanism all Sentry SDKs use when reporting errors.
*
* Endpoint pattern:
* POST https://<host>/api/<projectId>/envelope/
* ?sentry_key=<publicKey>&sentry_version=7
* Content-Type: application/x-sentry-envelope
*/
import { getEnvelopeEndpointWithUrlEncodedAuth, makeDsn } from "@sentry/core";
import { ApiError, ConfigError, ValidationError } from "../errors.js";
import { logger } from "../logger.js";
const log = logger.withTag("envelope.transport");
/** Client name passed to getEnvelopeEndpointWithUrlEncodedAuth, which appends /<version> internally. */
const SENTRY_CLIENT = "sentry-cli";
/** Flags subset relevant to DSN resolution. */
export type DsnFlags = {
dsn?: string;
};
/**
* Build the ingest URL for a given DSN.
*
* Returns the full URL including auth query params, ready to POST to.
* Throws ValidationError on an unparseable DSN.
*/
export function buildEnvelopeUrl(dsn: string): string {
let dsnComponents: ReturnType<typeof makeDsn>;
try {
dsnComponents = makeDsn(dsn);
} catch (err) {
log.debug("makeDsn threw for DSN input", err);
dsnComponents = undefined;
}
if (!dsnComponents) {
throw new ValidationError(`Invalid DSN: ${dsn}`, "dsn");
}
return getEnvelopeEndpointWithUrlEncodedAuth(dsnComponents, undefined, {
name: SENTRY_CLIENT,
version: "dev",
});
}
/**
* Resolve the DSN to use for sending, in priority order:
* 1. `--dsn` flag
* 2. `SENTRY_DSN` environment variable
* 3. Returns `undefined` (caller decides whether to auto-detect or error)
*/
export function resolveDsn(flags: DsnFlags): string | undefined {
if (flags.dsn) {
return flags.dsn.trim();
}
const envDsn = process.env.SENTRY_DSN;
if (envDsn) {
return envDsn.trim();
}
return;
}
/**
* Require a DSN to be available, throwing a helpful ConfigError if not.
*
* Auto-detection via project scanning is intentionally deferred — callers
* that want it can call the DSN detector before this.
*
* @param flags - DSN flag source (`--dsn`), with `SENTRY_DSN` fallback.
* @param usageHint - Optional command-specific usage example shown in the
* error (e.g. `"sentry monitor run <slug> -- <command>"`). Defaults to the
* `event send` example.
*/
export function requireDsn(
flags: DsnFlags,
usageHint = "sentry event send --dsn <your-dsn>"
): string {
const dsn = resolveDsn(flags);
if (dsn) {
return dsn;
}
throw new ConfigError(
"No DSN found. Provide one via --dsn <dsn> or set the SENTRY_DSN environment variable.",
usageHint
);
}
/**
* Read a file's bytes, throwing a clean ValidationError on ENOENT or I/O errors.
*
* Centralises the file-reading error-handling pattern used by
* `event send` (and previously by `send-envelope`).
*/
export async function readFileBytes(file: string): Promise<Uint8Array> {
const { readFile } = await import("node:fs/promises");
try {
return await readFile(file);
} catch (err) {
const code = (err as NodeJS.ErrnoException).code;
Eif (code === "ENOENT") {
throw new ValidationError(`File not found: ${file}`, "path");
}
throw new ValidationError(
`Cannot read file ${file}: ${(err as Error).message}`,
"path"
);
}
}
/**
* POST a serialized envelope to Sentry's ingest endpoint using DSN auth.
*
* No Bearer token is required — the DSN public key serves as authentication.
* Throws ApiError on non-2xx responses.
*/
export async function sendEnvelopeRequest(
dsn: string,
body: string | Uint8Array
): Promise<void> {
const url = buildEnvelopeUrl(dsn);
const response = await fetch(
new Request(url, {
method: "POST",
headers: { "Content-Type": "application/x-sentry-envelope" },
body,
})
);
if (!response.ok) {
let detail = `HTTP ${response.status}`;
try {
const json = (await response.json()) as Record<string, unknown>;
Eif (typeof json.detail === "string") {
detail = json.detail;
}
} catch (err) {
log.debug("Non-JSON error body, using HTTP status message", err);
}
throw new ApiError(detail, response.status, detail, url);
}
}
|