@@ -13,7 +13,9 @@ import {
1313} from "../../lib/api-client.js" ;
1414import { parseOrgProjectArg } from "../../lib/arg-parsing.js" ;
1515import { escapeMarkdownCell } from "../../lib/formatters/markdown.js" ;
16+ import { fmtPct } from "../../lib/formatters/numbers.js" ;
1617import { CommandOutput } from "../../lib/formatters/output.js" ;
18+ import { sparkline } from "../../lib/formatters/sparkline.js" ;
1719import { type Column , formatTable } from "../../lib/formatters/table.js" ;
1820import { formatRelativeTime } from "../../lib/formatters/time-utils.js" ;
1921import {
@@ -28,7 +30,6 @@ import {
2830 type ListResult ,
2931 type OrgListConfig ,
3032} from "../../lib/org-list.js" ;
31- import { fmtPct } from "./format.js" ;
3233
3334export const PAGINATION_KEY = "release-list" ;
3435
@@ -43,20 +44,40 @@ const VALID_SORT_VALUES: ReleaseSortValue[] = [
4344 "crash_free_users" ,
4445] ;
4546
47+ /**
48+ * Short aliases for sort values.
49+ *
50+ * Accepted alongside the canonical API values for convenience:
51+ * - `stable_sessions` / `cfs` → `crash_free_sessions`
52+ * - `stable_users` / `cfu` → `crash_free_users`
53+ */
54+ const SORT_ALIASES : Record < string , ReleaseSortValue > = {
55+ stable_sessions : "crash_free_sessions" ,
56+ stable_users : "crash_free_users" ,
57+ cfs : "crash_free_sessions" ,
58+ cfu : "crash_free_users" ,
59+ } ;
60+
4661const DEFAULT_SORT : ReleaseSortValue = "date" ;
4762
4863/**
4964 * Parse and validate the `--sort` flag value.
5065 *
51- * @throws Error when value is not one of the accepted sort keys
66+ * Accepts canonical API values and short aliases.
67+ * @throws Error when value is not recognized
5268 */
5369function parseSortFlag ( value : string ) : ReleaseSortValue {
5470 if ( VALID_SORT_VALUES . includes ( value as ReleaseSortValue ) ) {
5571 return value as ReleaseSortValue ;
5672 }
57- throw new Error (
58- `Invalid sort value. Must be one of: ${ VALID_SORT_VALUES . join ( ", " ) } `
73+ const alias = SORT_ALIASES [ value ] ;
74+ if ( alias ) {
75+ return alias ;
76+ }
77+ const allAccepted = [ ...VALID_SORT_VALUES , ...Object . keys ( SORT_ALIASES ) ] . join (
78+ ", "
5979 ) ;
80+ throw new Error ( `Invalid sort value. Must be one of: ${ allAccepted } ` ) ;
6081}
6182
6283/**
@@ -69,6 +90,29 @@ function getHealthData(release: OrgReleaseResponse) {
6990 return release . projects ?. find ( ( p ) => p . healthData ?. hasHealthData ) ?. healthData ;
7091}
7192
93+ /**
94+ * Extract session time-series data points from health stats.
95+ *
96+ * The `stats` object follows the same `{ "<period>": [[ts, count], ...] }`
97+ * shape as issue stats. Takes the first available key.
98+ */
99+ function extractSessionPoints ( stats ?: Record < string , unknown > ) : number [ ] {
100+ if ( ! stats ) {
101+ return [ ] ;
102+ }
103+ const key = Object . keys ( stats ) [ 0 ] ;
104+ if ( ! key ) {
105+ return [ ] ;
106+ }
107+ const buckets = stats [ key ] ;
108+ if ( ! Array . isArray ( buckets ) ) {
109+ return [ ] ;
110+ }
111+ return buckets . map ( ( b : unknown ) =>
112+ Array . isArray ( b ) && b . length >= 2 ? Number ( b [ 1 ] ) || 0 : 0
113+ ) ;
114+ }
115+
72116const RELEASE_COLUMNS : Column < ReleaseWithOrg > [ ] = [
73117 { header : "ORG" , value : ( r ) => r . orgSlug || "" } ,
74118 {
@@ -89,12 +133,24 @@ const RELEASE_COLUMNS: Column<ReleaseWithOrg>[] = [
89133 value : ( r ) => fmtPct ( getHealthData ( r ) ?. crashFreeSessions ) ,
90134 align : "right" ,
91135 } ,
136+ {
137+ header : "SESSIONS" ,
138+ value : ( r ) => {
139+ const health = getHealthData ( r ) ;
140+ if ( ! health ) {
141+ return "" ;
142+ }
143+ const points = extractSessionPoints (
144+ health . stats as Record < string , unknown > | undefined
145+ ) ;
146+ return points . length > 0 ? sparkline ( points ) : "" ;
147+ } ,
148+ } ,
92149 {
93150 header : "ISSUES" ,
94151 value : ( r ) => String ( r . newGroups ?? 0 ) ,
95152 align : "right" ,
96153 } ,
97- { header : "COMMITS" , value : ( r ) => String ( r . commitCount ?? 0 ) } ,
98154 { header : "DEPLOYS" , value : ( r ) => String ( r . deployCount ?? 0 ) } ,
99155] ;
100156
@@ -168,8 +224,8 @@ export const listCommand = buildListCommand("release", {
168224 " date # by creation date (default)\n" +
169225 " sessions # by total sessions\n" +
170226 " users # by total users\n" +
171- " crash_free_sessions # by crash-free session rate\n" +
172- " crash_free_users # by crash-free user rate\n\n" +
227+ " crash_free_sessions # by crash-free session rate (aliases: stable_sessions, cfs) \n" +
228+ " crash_free_users # by crash-free user rate (aliases: stable_users, cfu) \n\n" +
173229 "Target specification:\n" +
174230 " sentry release list # auto-detect from DSN or config\n" +
175231 " sentry release list <org>/ # list all releases in org (paginated)\n" +
@@ -199,7 +255,7 @@ export const listCommand = buildListCommand("release", {
199255 kind : "parsed" as const ,
200256 parse : parseSortFlag ,
201257 brief :
202- "Sort order : date, sessions, users, crash_free_sessions, crash_free_users" ,
258+ "Sort: date, sessions, users, crash_free_sessions (cfs) , crash_free_users (cfu) " ,
203259 default : DEFAULT_SORT ,
204260 } ,
205261 } ,
0 commit comments