vercel
Version:
The command-line interface for Vercel
960 lines (953 loc) • 28.1 kB
JavaScript
import { createRequire as __createRequire } from 'node:module';
import { fileURLToPath as __fileURLToPath } from 'node:url';
import { dirname as __dirname_ } from 'node:path';
const require = __createRequire(import.meta.url);
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __dirname_(__filename);
import {
fetchMetricDetailOrExit,
formatErrorJson,
formatQueryJson,
getDefaultAggregation,
getRollupColumnName,
handleApiError
} from "./chunk-ZINNI4TC.js";
import {
indent_default
} from "./chunk-A3NYPUKZ.js";
import {
resolveTimeRange,
validateAllProjectMutualExclusivity
} from "./chunk-HTOH3MSD.js";
import {
validateJsonOutput
} from "./chunk-XPKWKPWA.js";
import {
metricsCommand
} from "./chunk-IFATV36R.js";
import {
getScope
} from "./chunk-KWDV5FZH.js";
import {
getLinkedProject,
getProjectByNameOrId
} from "./chunk-X775BOSL.js";
import "./chunk-4OEA5ILS.js";
import "./chunk-ULXHXZCZ.js";
import {
require_ms
} from "./chunk-CO5D46AG.js";
import "./chunk-N2T234LO.js";
import {
table
} from "./chunk-DKD6GTQT.js";
import {
getFlagsSpecification,
parseArguments,
printError
} from "./chunk-4GQQJY5Y.js";
import {
ProjectNotFound,
isAPIError
} from "./chunk-UGXBNJMO.js";
import "./chunk-P4QNYOFB.js";
import {
output_manager_default
} from "./chunk-ZQKJVHXY.js";
import {
require_source
} from "./chunk-S7KYDPEM.js";
import {
__toESM
} from "./chunk-TZ2YI2VH.js";
// src/commands/metrics/validation.ts
function validateMutualExclusivity(all, project) {
return validateAllProjectMutualExclusivity(all, project);
}
function validateRequiredMetric(metric) {
if (metric) {
return { valid: true, value: metric };
}
return {
valid: false,
code: "MISSING_METRIC",
message: "Missing required metric. Specify the metric to query.\n\nRun 'vercel metrics schema' to see available metrics."
};
}
// src/commands/metrics/text-output.ts
var import_chalk = __toESM(require_source(), 1);
// src/commands/metrics/time-utils.ts
var import_ms = __toESM(require_ms(), 1);
var MINUTE_MS = 60 * 1e3;
var HOUR_MS = 60 * MINUTE_MS;
var DAY_MS = 24 * HOUR_MS;
function toGranularityDuration(input) {
const milliseconds = (0, import_ms.default)(input);
if (milliseconds === void 0) {
throw new Error(
`Invalid granularity format "${input}". Use 1m, 5m, 15m, 1h, 4h, 1d.`
);
}
if (milliseconds >= DAY_MS) {
return { days: milliseconds / DAY_MS };
}
if (milliseconds >= HOUR_MS) {
return { hours: milliseconds / HOUR_MS };
}
return { minutes: milliseconds / MINUTE_MS };
}
function toGranularityMs(input) {
const milliseconds = (0, import_ms.default)(input);
if (milliseconds === void 0) {
throw new Error(`Invalid granularity format "${input}".`);
}
return milliseconds;
}
var GRANULARITY_THRESHOLDS = [
[1 * HOUR_MS, "1m", "1m"],
// ≤1h
[2 * HOUR_MS, "5m", "5m"],
// ≤2h
[12 * HOUR_MS, "15m", "5m"],
// ≤12h
[3 * DAY_MS, "1h", "1h"],
// ≤3d
[30 * DAY_MS, "4h", "4h"]
// ≤30d
];
var FALLBACK_GRANULARITY = "1d";
function getAutoGranularity(rangeMs) {
for (const [maxRange, defaultG] of GRANULARITY_THRESHOLDS) {
if (rangeMs <= maxRange) {
return defaultG;
}
}
return FALLBACK_GRANULARITY;
}
function getMinGranularity(rangeMs) {
for (const [maxRange, , minG] of GRANULARITY_THRESHOLDS) {
if (rangeMs <= maxRange) {
return minG;
}
}
return FALLBACK_GRANULARITY;
}
function computeGranularity(rangeMs, explicit) {
if (!explicit) {
const auto = getAutoGranularity(rangeMs);
return {
duration: toGranularityDuration(auto),
adjusted: false
};
}
const minG = getMinGranularity(rangeMs);
const explicitMs = toGranularityMs(explicit);
const minMs = toGranularityMs(minG);
if (explicitMs < minMs) {
const rangeDays = Math.round(rangeMs / DAY_MS);
const rangeHours = Math.round(rangeMs / HOUR_MS);
const rangeLabel = rangeDays >= 1 ? `${rangeDays}-day` : `${rangeHours}-hour`;
return {
duration: toGranularityDuration(minG),
adjusted: true,
notice: `Granularity adjusted from ${explicit} to ${minG} for a ${rangeLabel} time range.`
};
}
return {
duration: toGranularityDuration(explicit),
adjusted: false
};
}
function roundTimeBoundaries(start, end, granularityMs) {
const flooredStart = new Date(
Math.floor(start.getTime() / granularityMs) * granularityMs
);
const ceiledEnd = new Date(
Math.ceil(end.getTime() / granularityMs) * granularityMs
);
return { start: flooredStart, end: ceiledEnd };
}
function toGranularityMsFromDuration(duration) {
if ("minutes" in duration) {
return duration.minutes * MINUTE_MS;
}
if ("hours" in duration) {
return duration.hours * HOUR_MS;
}
return duration.days * DAY_MS;
}
// src/commands/metrics/text-output.ts
var GROUP_KEY_DELIMITER = "";
var MAX_SPARKLINE_LENGTH = 120;
var COUNT_UNITS = /* @__PURE__ */ new Set(["count", "usd", "us dollars", "dollars"]);
var DURATION_UNITS = /* @__PURE__ */ new Set(["milliseconds", "seconds"]);
var BYTES_UNITS = /* @__PURE__ */ new Set([
"bytes",
"megabytes",
"gigabyte hour",
"gigabyte_hour",
"gigabyte hours",
"gigabyte_hours"
]);
var RATIO_UNITS = /* @__PURE__ */ new Set(["ratio", "percent"]);
var BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
var MISSING_CHAR = "\xB7";
function normalizeUnit(unit) {
return unit.trim().toLowerCase().replace(/[_\s]+/g, " ");
}
function toGroupKey(groupValues) {
if (groupValues.length === 0) {
return "";
}
return groupValues.join(GROUP_KEY_DELIMITER);
}
function pad2(n) {
return String(n).padStart(2, "0");
}
function pad4(n) {
return String(n).padStart(4, "0");
}
function formatHumanMinute(date) {
return `${pad4(date.getUTCFullYear())}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())} ${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}`;
}
function formatPeriodBound(input) {
const date = new Date(input);
if (isNaN(date.getTime())) {
return input;
}
return formatHumanMinute(date);
}
function formatGranularity(granularity) {
if ("minutes" in granularity) {
return `${granularity.minutes}m`;
}
if ("hours" in granularity) {
return `${granularity.hours}h`;
}
return `${granularity.days}d`;
}
function formatUnitLabel(unit) {
switch (normalizeUnit(unit)) {
case "milliseconds":
return "ms";
case "seconds":
return "s";
case "usd":
case "us dollars":
return "USD";
case "gigabyte hour":
case "gigabyte hours":
return "GB-h";
default:
return unit;
}
}
function isCountIntegerDisplay(measureType, aggregation) {
return measureType === "count" && aggregation === "sum";
}
function formatNumber(value, measureType, aggregation, opts) {
if (isCountIntegerDisplay(measureType, aggregation)) {
if (opts?.preserveFractionalCountSum && !Number.isInteger(value)) {
return formatDecimal(value);
}
return formatCount(value);
}
return formatDecimal(value);
}
function getStatColumns(aggregation) {
if (aggregation === "sum") {
return ["total", "avg", "min", "max"];
}
return ["avg", "min", "max"];
}
function toNumericValue(value) {
if (value === null || value === void 0) {
return null;
}
if (typeof value === "number") {
return Number.isFinite(value) ? value : null;
}
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
function isNonNullNumber(value) {
return value !== null;
}
function isPointWithValue(point) {
return point.value !== null;
}
function getOrCreate(map, key, make) {
const existing = map.get(key);
if (existing !== void 0) {
return existing;
}
const created = make();
map.set(key, created);
return created;
}
function getGroupFieldValue(row, field) {
const value = row[field];
return value == null || value === "" ? "(not set)" : String(value);
}
function normalizeTimestampToIso(timestamp) {
const parsed = Date.parse(timestamp);
if (isNaN(parsed)) {
return null;
}
return new Date(parsed).toISOString();
}
function formatStatCell(column, stats, measureType, aggregation, periodStart, periodEnd) {
switch (column) {
case "total":
return formatNumber(stats.total, measureType, aggregation);
case "avg":
return formatNumber(stats.avg, measureType, aggregation, {
preserveFractionalCountSum: true
});
case "min": {
const ts = formatMinMaxTimestamp(
new Date(stats.min.timestamp),
periodStart,
periodEnd
);
return `${formatNumber(stats.min.value, measureType, aggregation)} at ${ts}`;
}
case "max": {
const ts = formatMinMaxTimestamp(
new Date(stats.max.timestamp),
periodStart,
periodEnd
);
return `${formatNumber(stats.max.value, measureType, aggregation)} at ${ts}`;
}
}
}
function buildExpectedTimestamps(periodStart, periodEnd, granularityMs) {
const start = Date.parse(periodStart);
const end = Date.parse(periodEnd);
if (isNaN(start) || isNaN(end) || granularityMs <= 0 || end <= start) {
return [];
}
const timestamps = [];
for (let current = start; current < end; current += granularityMs) {
timestamps.push(new Date(current).toISOString());
}
return timestamps;
}
function getMeasureType(unit) {
const normalized = normalizeUnit(unit);
if (COUNT_UNITS.has(normalized)) {
return "count";
}
if (DURATION_UNITS.has(normalized)) {
return "duration";
}
if (BYTES_UNITS.has(normalized)) {
return "bytes";
}
if (RATIO_UNITS.has(normalized)) {
return "ratio";
}
return "ratio";
}
function formatCount(n) {
return Math.round(n).toLocaleString("en-US");
}
function formatDecimal(n) {
if (!Number.isFinite(n)) {
return String(n);
}
if (n === 0 || Object.is(n, -0)) {
return "0";
}
const sign = n < 0 ? "-" : "";
const abs = Math.abs(n);
if (abs >= 1) {
return `${sign}${abs.toFixed(1)}`;
}
const exponent = Math.floor(Math.log10(abs));
const decimals = Math.min(20, Math.max(2, -exponent + 1));
const fixed = abs.toFixed(decimals);
const trimmed = fixed.replace(/(\.\d*?[1-9])0+$/, "$1").replace(/\.0+$/, "").replace(/\.$/, "");
return `${sign}${trimmed}`;
}
function formatMinMaxTimestamp(date, periodStart, periodEnd) {
const sameDay = periodStart.getUTCFullYear() === periodEnd.getUTCFullYear() && periodStart.getUTCMonth() === periodEnd.getUTCMonth() && periodStart.getUTCDate() === periodEnd.getUTCDate();
if (sameDay) {
return `${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}`;
}
const sameYear = periodStart.getUTCFullYear() === periodEnd.getUTCFullYear();
if (sameYear) {
return `${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())} ${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}`;
}
return `${pad4(date.getUTCFullYear())}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())} ${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}`;
}
function extractGroupedSeries(data, groupBy, rollupColumn, periodStart, periodEnd, granularityMs) {
const expectedTimestamps = buildExpectedTimestamps(
periodStart,
periodEnd,
granularityMs
);
const groups = [];
const groupValues = /* @__PURE__ */ new Map();
const valueByGroup = /* @__PURE__ */ new Map();
for (const row of data) {
const values = groupBy.map((field) => getGroupFieldValue(row, field));
const key = toGroupKey(values);
if (!groupValues.has(key)) {
groups.push(key);
groupValues.set(key, values);
}
const groupMap = getOrCreate(valueByGroup, key, () => /* @__PURE__ */ new Map());
const rawTimestamp = row.timestamp;
if (rawTimestamp.length === 0) {
continue;
}
const timestamp = normalizeTimestampToIso(rawTimestamp);
if (!timestamp) {
continue;
}
const numeric = toNumericValue(row[rollupColumn]);
groupMap.set(timestamp, numeric);
}
const series = /* @__PURE__ */ new Map();
for (const key of groups) {
const byTimestamp = valueByGroup.get(key);
if (!byTimestamp) {
continue;
}
const points = expectedTimestamps.map((timestamp) => ({
timestamp,
value: byTimestamp.has(timestamp) ? byTimestamp.get(timestamp) ?? null : null
}));
series.set(key, points);
}
return { groups, series, groupValues };
}
function computeGroupStats(points) {
const present = points.filter(isPointWithValue);
if (present.length === 0) {
return {
total: 0,
avg: 0,
min: { value: 0, timestamp: "" },
max: { value: 0, timestamp: "" },
count: 0,
allMissing: true
};
}
let total = 0;
let min = present[0];
let max = present[0];
for (const point of present) {
total += point.value;
if (point.value < min.value) {
min = point;
}
if (point.value > max.value) {
max = point;
}
}
return {
total,
avg: total / present.length,
min: { value: min.value, timestamp: min.timestamp },
max: { value: max.value, timestamp: max.timestamp },
count: present.length,
allMissing: false
};
}
var MAX_GROUP_VALUE_LENGTH = 60;
function ellipsizeMiddle(str, maxLength) {
if (str.length <= maxLength)
return str;
const endLength = Math.floor((maxLength - 1) / 2);
const startLength = maxLength - 1 - endLength;
return `${str.slice(0, startLength)}\u2026${str.slice(str.length - endLength)}`;
}
function downsample(values, maxLen) {
if (maxLen <= 0) {
return [];
}
if (values.length <= maxLen) {
return [...values];
}
const result = [];
for (let i = 0; i < maxLen; i++) {
const start = Math.floor(i * values.length / maxLen);
const end = Math.floor((i + 1) * values.length / maxLen);
const bucket = values.slice(start, Math.max(start + 1, end));
const nullCount = bucket.filter((value) => value === null).length;
if (nullCount === bucket.length || nullCount > bucket.length / 2) {
result.push(null);
continue;
}
const present = bucket.filter(isNonNullNumber);
const avg = present.reduce((sum, value) => sum + value, 0) / present.length;
result.push(avg);
}
return result;
}
function generateSparkline(values) {
const sampled = downsample(values, MAX_SPARKLINE_LENGTH);
if (sampled.length === 0) {
return "";
}
const present = sampled.filter(isNonNullNumber);
if (present.length === 0) {
return sampled.map(() => MISSING_CHAR).join("");
}
const min = Math.min(...present);
const max = Math.max(...present);
if (min === max) {
const block = min === 0 ? BLOCKS[0] : BLOCKS[BLOCKS.length - 1];
return sampled.map((value) => value === null ? MISSING_CHAR : block).join("");
}
const range = max - min;
return sampled.map((value) => {
if (value === null) {
return MISSING_CHAR;
}
const ratio = (value - min) / range;
const index = Math.max(
0,
Math.min(BLOCKS.length - 1, Math.round(ratio * (BLOCKS.length - 1)))
);
return BLOCKS[index];
}).join("");
}
function formatMetadataHeader(opts) {
const rows = [
{
key: "Metric",
value: `${opts.metric} ${opts.aggregation}`
},
{
key: "Period",
value: `${formatPeriodBound(opts.periodStart)} to ${formatPeriodBound(opts.periodEnd)}`
},
{
key: "Interval",
value: formatGranularity(opts.granularity)
}
];
if (opts.filter) {
rows.push({ key: "Filter", value: opts.filter });
}
if (opts.scope.type === "project") {
rows.push({
key: "Project",
value: `${opts.projectName ?? opts.scope.projectIds[0]} (${opts.teamName ?? opts.scope.ownerId})`
});
} else {
rows.push({
key: "Team",
value: `${opts.teamName ?? opts.scope.ownerId} (all projects)`
});
}
if (opts.unit && normalizeUnit(opts.unit) !== "count") {
rows.push({ key: "Units", value: formatUnitLabel(opts.unit) });
}
if (typeof opts.groupCount === "number") {
rows.push({ key: "Groups", value: String(opts.groupCount) });
}
return rows.map((row) => `> ${import_chalk.default.gray(`${row.key}:`)} ${row.value}`).join("\n");
}
function formatSummaryTable(opts) {
const statColumns = getStatColumns(opts.aggregation);
const header = [...opts.groupByFields, ...statColumns];
const rows = [header.map((name) => import_chalk.default.bold(import_chalk.default.cyan(name)))];
for (const row of opts.rows) {
const nextRow = row.groupValues.map(
(v) => ellipsizeMiddle(v, MAX_GROUP_VALUE_LENGTH)
);
if (row.stats.allMissing) {
nextRow.push(...statColumns.map(() => "--"));
rows.push(nextRow);
continue;
}
nextRow.push(
...statColumns.map(
(column) => formatStatCell(
column,
row.stats,
opts.measureType,
opts.aggregation,
opts.periodStart,
opts.periodEnd
)
)
);
rows.push(nextRow);
}
const centeredColumns = /* @__PURE__ */ new Set(["min", "max"]);
const align = header.map(
(col) => centeredColumns.has(col) ? "c" : "r"
);
return indent_default(
table(rows, {
align,
hsep: 2
}),
2
);
}
function formatSparklineSection(groupRows, sparklines, groupByFields) {
const lines = ["sparklines:"];
if (groupRows.length === 0) {
const sparkline = sparklines[0];
if (sparkline) {
lines.push(indent_default(sparkline, 2));
}
return lines.join("\n");
}
const rowsWithSparklines = groupRows.map((groupValues, index) => ({
groupValues,
sparkline: sparklines[index] ?? ""
}));
const rows = [
[...groupByFields, "sparkline"].map((name) => import_chalk.default.bold(import_chalk.default.cyan(name))),
...rowsWithSparklines.map(({ groupValues, sparkline }) => [
...groupValues.map((v) => ellipsizeMiddle(v, MAX_GROUP_VALUE_LENGTH)),
sparkline
])
];
const align = groupByFields.map(() => "r");
align.push("l");
lines.push(
indent_default(
table(rows, {
align,
hsep: 2
}),
2
)
);
return lines.join("\n");
}
function getEffectiveDisplay(baseUnit, aggregation) {
switch (aggregation) {
case "percent":
return { displayUnit: "%", measureType: "ratio" };
case "persecond": {
const label = baseUnit ? formatUnitLabel(baseUnit) : void 0;
return {
displayUnit: label ? `${label}/s` : void 0,
measureType: getMeasureType(baseUnit ?? "ratio")
};
}
case "unique":
return { displayUnit: void 0, measureType: "count" };
default:
return {
displayUnit: baseUnit,
measureType: getMeasureType(baseUnit ?? "ratio")
};
}
}
function formatText(response, opts) {
const rollupColumn = getRollupColumnName(opts.metric, opts.aggregation);
const { displayUnit, measureType } = getEffectiveDisplay(
opts.metricUnit,
opts.aggregation
);
const granularityMs = toGranularityMsFromDuration(opts.granularity);
const { groups, series, groupValues } = extractGroupedSeries(
response.data ?? [],
opts.groupBy,
rollupColumn,
opts.periodStart,
opts.periodEnd,
granularityMs
);
const metadata = formatMetadataHeader({
metric: opts.metric,
aggregation: opts.aggregation,
periodStart: opts.periodStart,
periodEnd: opts.periodEnd,
granularity: opts.granularity,
filter: opts.filter,
scope: opts.scope,
projectName: opts.projectName,
teamName: opts.teamName,
unit: displayUnit,
groupCount: opts.groupBy.length > 0 ? groups.length : void 0
});
if (groups.length === 0) {
return `${metadata}
No data found for this period.
`;
}
const summaryRows = [];
const groupRows = [];
const sparklineRows = [];
for (const key of groups) {
const points = series.get(key) ?? [];
const values = points.map((point) => point.value);
const currentGroupValues = groupValues.get(key) ?? [];
summaryRows.push({
groupValues: currentGroupValues,
stats: computeGroupStats(points)
});
groupRows.push(currentGroupValues);
sparklineRows.push(generateSparkline(values));
}
const summaryTable = formatSummaryTable({
rows: summaryRows,
groupByFields: opts.groupBy,
measureType,
aggregation: opts.aggregation,
periodStart: new Date(opts.periodStart),
periodEnd: new Date(opts.periodEnd)
});
const groupedOutput = opts.groupBy.length > 0;
const sparklineSection = formatSparklineSection(
groupedOutput ? groupRows : [],
sparklineRows,
opts.groupBy
);
const sections = [metadata, summaryTable, sparklineSection];
return `${sections.join("\n\n")}
`;
}
// src/commands/metrics/query.ts
function handleValidationError(result, jsonOutput, client) {
if (jsonOutput) {
client.stdout.write(
formatErrorJson(result.code, result.message, result.allowedValues)
);
} else {
output_manager_default.error(result.message);
}
return 1;
}
async function resolveQueryScope(client, opts) {
if (opts.project || opts.all) {
const { team } = await getScope(client);
if (!team) {
const errMsg = "No team context found. Run `vercel switch` to select a team, or use `vercel link` in a project directory.";
if (opts.jsonOutput) {
client.stdout.write(formatErrorJson("NO_TEAM", errMsg));
} else {
output_manager_default.error(errMsg);
}
return 1;
}
if (opts.all) {
return {
scope: { type: "owner", ownerId: team.id },
accountId: team.id,
teamName: team.slug
};
}
const project = await getProjectByNameOrId(client, opts.project, team.id);
if (project instanceof ProjectNotFound) {
const errMsg = `Project "${opts.project}" was not found in team "${team.slug}".`;
if (opts.jsonOutput) {
client.stdout.write(formatErrorJson("PROJECT_NOT_FOUND", errMsg));
} else {
output_manager_default.error(errMsg);
}
return 1;
}
return {
scope: {
type: "project",
ownerId: team.id,
projectIds: [project.id]
},
accountId: team.id,
teamName: team.slug,
projectName: project.name
};
}
const linkedProject = await getLinkedProject(client);
if (linkedProject.status === "error") {
return linkedProject.exitCode;
}
if (linkedProject.status === "not_linked") {
const errMsg = "No linked project found. Run `vercel link` to link a project, or use --project <name-or-id> or --all.";
if (opts.jsonOutput) {
client.stdout.write(formatErrorJson("NOT_LINKED", errMsg));
} else {
output_manager_default.error(errMsg);
}
return 1;
}
return {
scope: {
type: "project",
ownerId: linkedProject.org.id,
projectIds: [linkedProject.project.id]
},
accountId: linkedProject.org.id,
teamName: linkedProject.org.slug,
projectName: linkedProject.project.name
};
}
async function query(client, telemetry) {
let parsedArgs;
const flagsSpecification = getFlagsSpecification(metricsCommand.options);
try {
parsedArgs = parseArguments(client.argv.slice(2), flagsSpecification);
} catch (err) {
printError(err);
return 1;
}
const flags = parsedArgs.flags;
const positionalArgs = parsedArgs.args.slice(1);
const positionalMetric = positionalArgs[0] === "query" ? positionalArgs[1] : positionalArgs[0];
const formatResult = validateJsonOutput(flags);
if (!formatResult.valid) {
output_manager_default.error(formatResult.error);
return 1;
}
const jsonOutput = formatResult.jsonOutput;
const metricFlag = positionalMetric;
const aggregationFlag = flags["--aggregation"];
const groupBy = flags["--group-by"] ?? [];
const limit = flags["--limit"];
const filter = flags["--filter"];
const since = flags["--since"];
const until = flags["--until"];
const granularity = flags["--granularity"];
const project = flags["--project"];
const all = flags["--all"];
telemetry.trackCliArgumentMetricId(metricFlag);
telemetry.trackCliOptionAggregation(aggregationFlag);
telemetry.trackCliOptionGroupBy(groupBy.length > 0 ? groupBy : void 0);
telemetry.trackCliOptionLimit(limit);
telemetry.trackCliOptionFilter(filter);
telemetry.trackCliOptionSince(since);
telemetry.trackCliOptionUntil(until);
telemetry.trackCliOptionGranularity(granularity);
telemetry.trackCliOptionProject(project);
telemetry.trackCliFlagAll(all);
telemetry.trackCliOptionFormat(flags["--format"]);
const requiredMetric = validateRequiredMetric(metricFlag);
if (!requiredMetric.valid) {
return handleValidationError(requiredMetric, jsonOutput, client);
}
const metric = requiredMetric.value;
const mutualResult = validateMutualExclusivity(all, project);
if (!mutualResult.valid) {
return handleValidationError(mutualResult, jsonOutput, client);
}
const scopeResult = await resolveQueryScope(client, {
project,
all,
jsonOutput
});
if (typeof scopeResult === "number") {
return scopeResult;
}
const { scope, accountId, teamName, projectName } = scopeResult;
const detailOrExitCode = await fetchMetricDetailOrExit(
client,
accountId,
metric,
jsonOutput
);
if (typeof detailOrExitCode === "number") {
return detailOrExitCode;
}
const aggregationInput = aggregationFlag ?? getDefaultAggregation(detailOrExitCode, metric) ?? "sum";
const aggregation = aggregationInput;
let startTime;
let endTime;
try {
({ startTime, endTime } = resolveTimeRange(since, until));
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
if (jsonOutput) {
client.stdout.write(formatErrorJson("INVALID_TIME", errMsg));
} else {
output_manager_default.error(errMsg);
}
return 1;
}
const rangeMs = endTime.getTime() - startTime.getTime();
const granResult = computeGranularity(rangeMs, granularity);
if (granResult.adjusted && granResult.notice) {
output_manager_default.log(`Notice: ${granResult.notice}`);
}
const rounded = roundTimeBoundaries(
startTime,
endTime,
toGranularityMsFromDuration(granResult.duration)
);
const body = {
scope,
metric,
aggregation,
startTime: rounded.start.toISOString(),
endTime: rounded.end.toISOString(),
granularity: granResult.duration,
...groupBy.length > 0 ? { groupBy } : {},
...filter ? { filter } : {},
limit: limit ?? 10
};
output_manager_default.spinner("Querying metrics...");
let response;
try {
response = await client.fetch(
"/v2/observability/query",
{
method: "POST",
body: JSON.stringify(body),
headers: { "Content-Type": "application/json" },
accountId,
bailOn429: true
}
);
} catch (err) {
if (isAPIError(err)) {
return handleApiError(err, jsonOutput, client);
}
const errMsg = err instanceof Error ? err.message : String(err);
if (jsonOutput) {
client.stdout.write(formatErrorJson("NETWORK_ERROR", errMsg));
} else {
output_manager_default.error(errMsg);
}
return 1;
} finally {
output_manager_default.stopSpinner();
}
if (jsonOutput) {
client.stdout.write(
formatQueryJson(
{
metric,
aggregation,
groupBy,
filter,
startTime: rounded.start.toISOString(),
endTime: rounded.end.toISOString(),
granularity: granResult.duration
},
response
)
);
} else {
client.stdout.write(
formatText(response, {
metric,
metricUnit: detailOrExitCode.find((item) => item.id === metric)?.unit ?? "count",
aggregation,
groupBy,
filter,
scope,
projectName,
teamName,
periodStart: rounded.start.toISOString(),
periodEnd: rounded.end.toISOString(),
granularity: granResult.duration
})
);
}
return 0;
}
export {
query as default
};