eas-cli 18.5.0 → 18.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +90 -89
  2. package/build/commandUtils/pagination.d.ts +2 -1
  3. package/build/commandUtils/pagination.js +3 -2
  4. package/build/commands/build/dev.d.ts +1 -0
  5. package/build/commands/build/dev.js +10 -2
  6. package/build/commands/deploy/index.js +18 -2
  7. package/build/commands/metadata/pull.d.ts +1 -1
  8. package/build/commands/metadata/pull.js +2 -4
  9. package/build/commands/metadata/push.d.ts +1 -1
  10. package/build/commands/metadata/push.js +2 -4
  11. package/build/commands/observe/events.d.ts +27 -0
  12. package/build/commands/observe/events.js +140 -0
  13. package/build/commands/observe/metrics.d.ts +21 -0
  14. package/build/commands/observe/metrics.js +111 -0
  15. package/build/commands/observe/versions.d.ts +19 -0
  16. package/build/commands/observe/versions.js +69 -0
  17. package/build/credentials/ios/IosCredentialsProvider.js +8 -4
  18. package/build/credentials/ios/utils/provisioningProfile.d.ts +1 -0
  19. package/build/credentials/ios/utils/provisioningProfile.js +15 -1
  20. package/build/graphql/generated.d.ts +634 -61
  21. package/build/graphql/generated.js +11 -9
  22. package/build/graphql/queries/ObserveQuery.d.ts +35 -0
  23. package/build/graphql/queries/ObserveQuery.js +109 -0
  24. package/build/graphql/types/Observe.d.ts +3 -0
  25. package/build/graphql/types/Observe.js +84 -0
  26. package/build/metadata/apple/config/reader.d.ts +9 -1
  27. package/build/metadata/apple/config/reader.js +25 -0
  28. package/build/metadata/apple/config/writer.d.ts +7 -1
  29. package/build/metadata/apple/config/writer.js +44 -0
  30. package/build/metadata/apple/data.d.ts +2 -1
  31. package/build/metadata/apple/tasks/app-clip.d.ts +37 -0
  32. package/build/metadata/apple/tasks/app-clip.js +404 -0
  33. package/build/metadata/apple/tasks/index.js +2 -0
  34. package/build/metadata/apple/tasks/previews.js +16 -12
  35. package/build/metadata/apple/tasks/screenshots.js +15 -4
  36. package/build/metadata/apple/types.d.ts +28 -1
  37. package/build/observe/fetchEvents.d.ts +27 -0
  38. package/build/observe/fetchEvents.js +83 -0
  39. package/build/observe/fetchMetrics.d.ts +11 -0
  40. package/build/observe/fetchMetrics.js +78 -0
  41. package/build/observe/fetchVersions.d.ts +7 -0
  42. package/build/observe/fetchVersions.js +31 -0
  43. package/build/observe/formatEvents.d.ts +31 -0
  44. package/build/observe/formatEvents.js +99 -0
  45. package/build/observe/formatMetrics.d.ts +38 -0
  46. package/build/observe/formatMetrics.js +206 -0
  47. package/build/observe/formatVersions.d.ts +32 -0
  48. package/build/observe/formatVersions.js +92 -0
  49. package/build/observe/metricNames.d.ts +4 -0
  50. package/build/observe/metricNames.js +33 -0
  51. package/build/observe/startAndEndTime.d.ts +18 -0
  52. package/build/observe/startAndEndTime.js +36 -0
  53. package/build/user/SessionManager.js +11 -0
  54. package/build/worker/upload.js +15 -5
  55. package/oclif.manifest.json +2102 -1620
  56. package/package.json +9 -6
  57. package/schema/metadata-0.json +177 -0
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateDateFlag = validateDateFlag;
4
+ exports.fetchObserveMetricsAsync = fetchObserveMetricsAsync;
5
+ const tslib_1 = require("tslib");
6
+ const errors_1 = require("../commandUtils/errors");
7
+ const generated_1 = require("../graphql/generated");
8
+ const ObserveQuery_1 = require("../graphql/queries/ObserveQuery");
9
+ const log_1 = tslib_1.__importDefault(require("../log"));
10
+ const formatMetrics_1 = require("./formatMetrics");
11
+ const appPlatformToObservePlatform = {
12
+ [generated_1.AppPlatform.Android]: generated_1.AppObservePlatform.Android,
13
+ [generated_1.AppPlatform.Ios]: generated_1.AppObservePlatform.Ios,
14
+ };
15
+ function validateDateFlag(value, flagName) {
16
+ const parsed = new Date(value);
17
+ if (isNaN(parsed.getTime())) {
18
+ throw new errors_1.EasCommandError(`Invalid ${flagName} date: "${value}". Provide a valid ISO 8601 date (e.g. 2025-01-01).`);
19
+ }
20
+ }
21
+ async function fetchObserveMetricsAsync(graphqlClient, appId, metricNames, platforms, startTime, endTime) {
22
+ const queries = platforms.map(async (appPlatform) => {
23
+ const observePlatform = appPlatformToObservePlatform[appPlatform];
24
+ try {
25
+ const appVersions = await ObserveQuery_1.ObserveQuery.appVersionsAsync(graphqlClient, {
26
+ appId,
27
+ platform: observePlatform,
28
+ startTime,
29
+ endTime,
30
+ metricNames,
31
+ });
32
+ return { appPlatform, appVersions };
33
+ }
34
+ catch (error) {
35
+ log_1.default.warn(`Failed to fetch observe data on ${observePlatform}: ${error.message}`);
36
+ return null;
37
+ }
38
+ });
39
+ const results = await Promise.all(queries);
40
+ const metricsMap = new Map();
41
+ const buildNumbersMap = new Map();
42
+ const updateIdsMap = new Map();
43
+ const totalEventCounts = new Map();
44
+ for (const result of results) {
45
+ if (!result) {
46
+ continue;
47
+ }
48
+ const { appPlatform, appVersions } = result;
49
+ for (const version of appVersions) {
50
+ const key = (0, formatMetrics_1.makeMetricsKey)(version.appVersion, appPlatform);
51
+ if (!metricsMap.has(key)) {
52
+ metricsMap.set(key, new Map());
53
+ }
54
+ if (!buildNumbersMap.has(key)) {
55
+ buildNumbersMap.set(key, version.buildNumbers.map(bn => bn.appBuildNumber));
56
+ }
57
+ if (!updateIdsMap.has(key)) {
58
+ updateIdsMap.set(key, version.updates.map(u => u.appUpdateId));
59
+ }
60
+ for (const metric of version.metrics) {
61
+ const values = {
62
+ min: metric.statistics.min,
63
+ max: metric.statistics.max,
64
+ median: metric.statistics.median,
65
+ average: metric.statistics.average,
66
+ p80: metric.statistics.p80,
67
+ p90: metric.statistics.p90,
68
+ p99: metric.statistics.p99,
69
+ eventCount: metric.eventCount,
70
+ };
71
+ metricsMap.get(key).set(metric.metricName, values);
72
+ const eventCountKey = `${metric.metricName}:${appPlatform}`;
73
+ totalEventCounts.set(eventCountKey, (totalEventCounts.get(eventCountKey) ?? 0) + metric.eventCount);
74
+ }
75
+ }
76
+ }
77
+ return { metricsMap, buildNumbersMap, updateIdsMap, totalEventCounts };
78
+ }
@@ -0,0 +1,7 @@
1
+ import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
2
+ import { AppObserveAppVersion, AppPlatform } from '../graphql/generated';
3
+ export interface AppVersionsResult {
4
+ platform: AppPlatform;
5
+ appVersions: AppObserveAppVersion[];
6
+ }
7
+ export declare function fetchObserveVersionsAsync(graphqlClient: ExpoGraphqlClient, appId: string, platforms: AppPlatform[], startTime: string, endTime: string): Promise<AppVersionsResult[]>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchObserveVersionsAsync = fetchObserveVersionsAsync;
4
+ const tslib_1 = require("tslib");
5
+ const generated_1 = require("../graphql/generated");
6
+ const ObserveQuery_1 = require("../graphql/queries/ObserveQuery");
7
+ const log_1 = tslib_1.__importDefault(require("../log"));
8
+ const appPlatformToObservePlatform = {
9
+ [generated_1.AppPlatform.Android]: generated_1.AppObservePlatform.Android,
10
+ [generated_1.AppPlatform.Ios]: generated_1.AppObservePlatform.Ios,
11
+ };
12
+ async function fetchObserveVersionsAsync(graphqlClient, appId, platforms, startTime, endTime) {
13
+ const queries = platforms.map(async (appPlatform) => {
14
+ const observePlatform = appPlatformToObservePlatform[appPlatform];
15
+ try {
16
+ const appVersions = await ObserveQuery_1.ObserveQuery.appVersionsAsync(graphqlClient, {
17
+ appId,
18
+ platform: observePlatform,
19
+ startTime,
20
+ endTime,
21
+ });
22
+ return { platform: appPlatform, appVersions };
23
+ }
24
+ catch (error) {
25
+ log_1.default.warn(`Failed to fetch app versions for ${observePlatform}: ${error.message}`);
26
+ return null;
27
+ }
28
+ });
29
+ const results = await Promise.all(queries);
30
+ return results.filter((r) => r !== null);
31
+ }
@@ -0,0 +1,31 @@
1
+ import { AppObserveEvent, PageInfo } from '../graphql/generated';
2
+ export interface ObserveEventJson {
3
+ id: string;
4
+ metricName: string;
5
+ metricValue: number;
6
+ appVersion: string;
7
+ appBuildNumber: string;
8
+ appUpdateId: string | null;
9
+ deviceModel: string;
10
+ deviceOs: string;
11
+ deviceOsVersion: string;
12
+ countryCode: string | null;
13
+ sessionId: string | null;
14
+ easClientId: string;
15
+ timestamp: string;
16
+ }
17
+ export interface BuildEventsTableOptions {
18
+ metricName: string;
19
+ daysBack?: number;
20
+ startTime?: string;
21
+ endTime?: string;
22
+ totalEventCount?: number;
23
+ }
24
+ export declare function buildObserveEventsTable(events: AppObserveEvent[], pageInfo: PageInfo, options?: BuildEventsTableOptions): string;
25
+ export declare function buildObserveEventsJson(events: AppObserveEvent[], pageInfo: PageInfo): {
26
+ events: ObserveEventJson[];
27
+ pageInfo: {
28
+ hasNextPage: boolean;
29
+ endCursor: string | null;
30
+ };
31
+ };
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildObserveEventsTable = buildObserveEventsTable;
4
+ exports.buildObserveEventsJson = buildObserveEventsJson;
5
+ const tslib_1 = require("tslib");
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const metricNames_1 = require("./metricNames");
8
+ function formatTimestamp(isoString) {
9
+ const date = new Date(isoString);
10
+ return date.toLocaleDateString('en-US', {
11
+ year: 'numeric',
12
+ month: 'short',
13
+ day: 'numeric',
14
+ hour: '2-digit',
15
+ minute: '2-digit',
16
+ });
17
+ }
18
+ function formatDate(isoString) {
19
+ const date = new Date(isoString);
20
+ return date.toLocaleDateString('en-US', {
21
+ year: 'numeric',
22
+ month: 'short',
23
+ day: 'numeric',
24
+ });
25
+ }
26
+ function buildObserveEventsTable(events, pageInfo, options) {
27
+ if (events.length === 0) {
28
+ return chalk_1.default.yellow('No events found.');
29
+ }
30
+ const hasUpdates = events.some(e => e.appUpdateId);
31
+ const headers = [
32
+ 'Value',
33
+ 'App Version',
34
+ ...(hasUpdates ? ['Update'] : []),
35
+ 'Platform',
36
+ 'Device',
37
+ 'Country',
38
+ 'Timestamp',
39
+ ];
40
+ const rows = events.map(event => [
41
+ `${event.metricValue.toFixed(2)}s`,
42
+ `${event.appVersion} (${event.appBuildNumber})`,
43
+ ...(hasUpdates ? [event.appUpdateId ?? '-'] : []),
44
+ `${event.deviceOs} ${event.deviceOsVersion}`,
45
+ event.deviceModel,
46
+ event.countryCode ?? '-',
47
+ formatTimestamp(event.timestamp),
48
+ ]);
49
+ const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map(r => r[i].length)));
50
+ const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(' ');
51
+ const separatorLine = colWidths.map(w => '-'.repeat(w)).join(' ');
52
+ const dataLines = rows.map(row => row.map((cell, i) => cell.padEnd(colWidths[i])).join(' '));
53
+ const lines = [];
54
+ if (options) {
55
+ const metricDisplay = (0, metricNames_1.getMetricDisplayName)(options.metricName);
56
+ let timeDesc;
57
+ if (options.daysBack) {
58
+ timeDesc = `for the last ${options.daysBack} days`;
59
+ }
60
+ else if (options.startTime && options.endTime) {
61
+ timeDesc = `from ${formatDate(options.startTime)} to ${formatDate(options.endTime)}`;
62
+ }
63
+ else {
64
+ timeDesc = '';
65
+ }
66
+ const totalDesc = options.totalEventCount != null
67
+ ? ` — ${options.totalEventCount.toLocaleString()} total events`
68
+ : '';
69
+ lines.push(chalk_1.default.bold(`${metricDisplay} events ${timeDesc}${totalDesc}`.trim()), '');
70
+ }
71
+ lines.push(chalk_1.default.bold(headerLine), separatorLine, ...dataLines);
72
+ if (pageInfo.hasNextPage && pageInfo.endCursor) {
73
+ lines.push('', `Next page: --after ${pageInfo.endCursor}`);
74
+ }
75
+ return lines.join('\n');
76
+ }
77
+ function buildObserveEventsJson(events, pageInfo) {
78
+ return {
79
+ events: events.map(event => ({
80
+ id: event.id,
81
+ metricName: event.metricName,
82
+ metricValue: event.metricValue,
83
+ appVersion: event.appVersion,
84
+ appBuildNumber: event.appBuildNumber,
85
+ appUpdateId: event.appUpdateId ?? null,
86
+ deviceModel: event.deviceModel,
87
+ deviceOs: event.deviceOs,
88
+ deviceOsVersion: event.deviceOsVersion,
89
+ countryCode: event.countryCode ?? null,
90
+ sessionId: event.sessionId ?? null,
91
+ easClientId: event.easClientId,
92
+ timestamp: event.timestamp,
93
+ })),
94
+ pageInfo: {
95
+ hasNextPage: pageInfo.hasNextPage,
96
+ endCursor: pageInfo.endCursor ?? null,
97
+ },
98
+ };
99
+ }
@@ -0,0 +1,38 @@
1
+ import { AppPlatform } from '../graphql/generated';
2
+ export type StatisticKey = 'min' | 'max' | 'median' | 'average' | 'p80' | 'p90' | 'p99' | 'eventCount';
3
+ export declare const STAT_ALIASES: Record<string, StatisticKey>;
4
+ export declare const STAT_DISPLAY_NAMES: Record<StatisticKey, string>;
5
+ export declare function resolveStatKey(input: string): StatisticKey;
6
+ export interface MetricValues {
7
+ min: number | null | undefined;
8
+ max: number | null | undefined;
9
+ median: number | null | undefined;
10
+ average: number | null | undefined;
11
+ p80: number | null | undefined;
12
+ p90: number | null | undefined;
13
+ p99: number | null | undefined;
14
+ eventCount: number | null | undefined;
15
+ }
16
+ type ObserveMetricsKey = `${string}:${AppPlatform}`;
17
+ export type ObserveMetricsMap = Map<ObserveMetricsKey, Map<string, MetricValues>>;
18
+ export type BuildNumbersMap = Map<ObserveMetricsKey, string[]>;
19
+ export type UpdateIdsMap = Map<ObserveMetricsKey, string[]>;
20
+ export declare function makeMetricsKey(appVersion: string, platform: AppPlatform): ObserveMetricsKey;
21
+ export type MetricValuesJson = Partial<Record<StatisticKey, number | null>>;
22
+ export interface ObserveMetricsVersionResult {
23
+ appVersion: string;
24
+ platform: AppPlatform;
25
+ metrics: Record<string, MetricValuesJson>;
26
+ }
27
+ export interface ObserveMetricsJsonOutput {
28
+ versions: ObserveMetricsVersionResult[];
29
+ totalEventCounts: Record<string, Record<string, number>>;
30
+ }
31
+ export declare function buildObserveMetricsJson(metricsMap: ObserveMetricsMap, metricNames: string[], stats: StatisticKey[], totalEventCounts?: Map<string, number>): ObserveMetricsJsonOutput;
32
+ export declare function buildObserveMetricsTable(metricsMap: ObserveMetricsMap, metricNames: string[], stats: StatisticKey[], options?: {
33
+ daysBack?: number;
34
+ buildNumbersMap?: BuildNumbersMap;
35
+ updateIdsMap?: UpdateIdsMap;
36
+ totalEventCounts?: Map<string, number>;
37
+ }): string;
38
+ export {};
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STAT_DISPLAY_NAMES = exports.STAT_ALIASES = void 0;
4
+ exports.resolveStatKey = resolveStatKey;
5
+ exports.makeMetricsKey = makeMetricsKey;
6
+ exports.buildObserveMetricsJson = buildObserveMetricsJson;
7
+ exports.buildObserveMetricsTable = buildObserveMetricsTable;
8
+ const tslib_1 = require("tslib");
9
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
10
+ const errors_1 = require("../commandUtils/errors");
11
+ const platform_1 = require("../platform");
12
+ const metricNames_1 = require("./metricNames");
13
+ exports.STAT_ALIASES = {
14
+ min: 'min',
15
+ max: 'max',
16
+ med: 'median',
17
+ median: 'median',
18
+ avg: 'average',
19
+ average: 'average',
20
+ p80: 'p80',
21
+ p90: 'p90',
22
+ p99: 'p99',
23
+ count: 'eventCount',
24
+ event_count: 'eventCount',
25
+ eventCount: 'eventCount',
26
+ };
27
+ exports.STAT_DISPLAY_NAMES = {
28
+ min: 'Min',
29
+ max: 'Max',
30
+ median: 'Med',
31
+ average: 'Avg',
32
+ p80: 'P80',
33
+ p90: 'P90',
34
+ p99: 'P99',
35
+ eventCount: 'Count',
36
+ };
37
+ function resolveStatKey(input) {
38
+ const resolved = exports.STAT_ALIASES[input];
39
+ if (resolved) {
40
+ return resolved;
41
+ }
42
+ throw new errors_1.EasCommandError(`Unknown statistic: "${input}". Valid options: ${Object.keys(exports.STAT_ALIASES).join(', ')}`);
43
+ }
44
+ function formatStatValue(stat, value) {
45
+ if (value == null) {
46
+ return '-';
47
+ }
48
+ if (stat === 'eventCount') {
49
+ return String(value);
50
+ }
51
+ return `${value.toFixed(2)}s`;
52
+ }
53
+ function formatMergedCell(stat, statValue, eventCount) {
54
+ const formatted = formatStatValue(stat, statValue);
55
+ const count = eventCount != null ? String(eventCount) : '-';
56
+ return `${formatted} (${count})`;
57
+ }
58
+ function makeMetricsKey(appVersion, platform) {
59
+ return `${appVersion}:${platform}`;
60
+ }
61
+ function parseMetricsKey(key) {
62
+ const lastColon = key.lastIndexOf(':');
63
+ return {
64
+ appVersion: key.slice(0, lastColon),
65
+ platform: key.slice(lastColon + 1),
66
+ };
67
+ }
68
+ function buildObserveMetricsJson(metricsMap, metricNames, stats, totalEventCounts) {
69
+ const versions = [];
70
+ for (const [key, versionMetrics] of metricsMap) {
71
+ const { appVersion, platform } = parseMetricsKey(key);
72
+ const metrics = {};
73
+ for (const metricName of metricNames) {
74
+ const values = versionMetrics.get(metricName);
75
+ const statValues = {};
76
+ for (const stat of stats) {
77
+ statValues[stat] = values?.[stat] ?? null;
78
+ }
79
+ metrics[metricName] = statValues;
80
+ }
81
+ versions.push({ appVersion, platform, metrics });
82
+ }
83
+ // Group total event counts by metric → platform
84
+ const counts = {};
85
+ if (totalEventCounts) {
86
+ for (const [key, count] of totalEventCounts) {
87
+ const lastColon = key.lastIndexOf(':');
88
+ const metricName = key.slice(0, lastColon);
89
+ const platform = key.slice(lastColon + 1);
90
+ if (!counts[metricName]) {
91
+ counts[metricName] = {};
92
+ }
93
+ counts[metricName][platform] = count;
94
+ }
95
+ }
96
+ return { versions, totalEventCounts: counts };
97
+ }
98
+ function buildStatsDescription(displayStats) {
99
+ return displayStats.map(s => exports.STAT_DISPLAY_NAMES[s]).join(', ');
100
+ }
101
+ function buildTimeRangeDescription(daysBack) {
102
+ if (daysBack) {
103
+ return `for the last ${daysBack} days`;
104
+ }
105
+ return '';
106
+ }
107
+ function renderTable(headers, rows, footerRow) {
108
+ const allRows = footerRow ? [...rows, footerRow] : rows;
109
+ const colWidths = headers.map((h, i) => Math.max(h.length, ...allRows.map(r => (r[i] ?? '').length)));
110
+ const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(' ');
111
+ const separatorLine = colWidths.map(w => '-'.repeat(w)).join(' ');
112
+ const dataLines = rows.map(row => row.map((cell, i) => cell.padEnd(colWidths[i])).join(' '));
113
+ const lines = [chalk_1.default.bold(headerLine), separatorLine, ...dataLines];
114
+ if (footerRow) {
115
+ lines.push(separatorLine);
116
+ lines.push(footerRow.map((cell, i) => cell.padEnd(colWidths[i])).join(' '));
117
+ }
118
+ return lines.join('\n');
119
+ }
120
+ function buildObserveMetricsTable(metricsMap, metricNames, stats, options) {
121
+ const { versions: results } = buildObserveMetricsJson(metricsMap, metricNames, stats);
122
+ if (results.length === 0) {
123
+ return chalk_1.default.yellow('No metrics data found.');
124
+ }
125
+ const displayStats = stats.filter(s => s !== 'eventCount');
126
+ const hasEventCount = stats.includes('eventCount');
127
+ // Build summary header
128
+ const statsDesc = displayStats.length > 0 ? buildStatsDescription(displayStats) : 'Event count';
129
+ const timeDesc = buildTimeRangeDescription(options?.daysBack);
130
+ const countSuffix = hasEventCount && displayStats.length > 0 ? ' (event count)' : '';
131
+ const summaryLine = `${statsDesc} values${countSuffix}${timeDesc ? ` ${timeDesc}` : ''}`;
132
+ // Group results by platform
133
+ const byPlatform = new Map();
134
+ for (const result of results) {
135
+ if (!byPlatform.has(result.platform)) {
136
+ byPlatform.set(result.platform, []);
137
+ }
138
+ byPlatform.get(result.platform).push(result);
139
+ }
140
+ // Build metric column headers
141
+ const metricHeaders = [];
142
+ for (const m of metricNames) {
143
+ const name = (0, metricNames_1.getMetricDisplayName)(m);
144
+ if (displayStats.length > 0 && hasEventCount) {
145
+ // Merged mode: one column per metric
146
+ metricHeaders.push(name);
147
+ }
148
+ else {
149
+ // Separate columns per stat
150
+ for (const stat of displayStats.length > 0
151
+ ? displayStats
152
+ : ['eventCount']) {
153
+ metricHeaders.push(`${name} ${exports.STAT_DISPLAY_NAMES[stat]}`);
154
+ }
155
+ }
156
+ }
157
+ // Check if any version has updates
158
+ const hasUpdates = options?.updateIdsMap
159
+ ? Array.from(options.updateIdsMap.values()).some(ids => ids.length > 0)
160
+ : false;
161
+ const headers = ['App Version', ...(hasUpdates ? ['Updates'] : []), ...metricHeaders];
162
+ const sections = [chalk_1.default.bold(summaryLine)];
163
+ for (const [platform, platformResults] of byPlatform) {
164
+ sections.push('');
165
+ sections.push(chalk_1.default.bold(platform_1.appPlatformDisplayNames[platform]));
166
+ const rows = platformResults.map(result => {
167
+ const key = makeMetricsKey(result.appVersion, result.platform);
168
+ const buildNumbers = options?.buildNumbersMap?.get(key);
169
+ const versionLabel = buildNumbers?.length
170
+ ? `${result.appVersion} (${buildNumbers.join(', ')})`
171
+ : result.appVersion;
172
+ const updateIds = options?.updateIdsMap?.get(key);
173
+ const updatesLabel = updateIds?.length ? updateIds.join(', ') : '';
174
+ const metricCells = [];
175
+ for (const m of metricNames) {
176
+ const values = result.metrics[m];
177
+ if (displayStats.length > 0 && hasEventCount) {
178
+ for (const stat of displayStats) {
179
+ metricCells.push(formatMergedCell(stat, values?.[stat] ?? null, values?.eventCount ?? null));
180
+ }
181
+ }
182
+ else {
183
+ for (const stat of displayStats.length > 0
184
+ ? displayStats
185
+ : ['eventCount']) {
186
+ metricCells.push(formatStatValue(stat, values?.[stat] ?? null));
187
+ }
188
+ }
189
+ }
190
+ return [versionLabel, ...(hasUpdates ? [updatesLabel] : []), ...metricCells];
191
+ });
192
+ let footerRow;
193
+ if (options?.totalEventCounts) {
194
+ const countCells = [];
195
+ for (const m of metricNames) {
196
+ const count = options.totalEventCounts.get(`${m}:${platform}`);
197
+ countCells.push(count != null ? count.toLocaleString() : '-');
198
+ }
199
+ if (countCells.some(c => c !== '-')) {
200
+ footerRow = ['Total events', ...(hasUpdates ? [''] : []), ...countCells];
201
+ }
202
+ }
203
+ sections.push(renderTable(headers, rows, footerRow));
204
+ }
205
+ return sections.join('\n');
206
+ }
@@ -0,0 +1,32 @@
1
+ import { AppVersionsResult } from './fetchVersions';
2
+ export interface AppVersionJson {
3
+ platform: string;
4
+ appVersion: string;
5
+ firstSeenAt: string;
6
+ eventCount: number;
7
+ uniqueUserCount: number;
8
+ buildNumbers: AppBuildNumberJson[];
9
+ updates: AppUpdateJson[];
10
+ }
11
+ export interface AppBuildNumberJson {
12
+ appBuildNumber: string;
13
+ firstSeenAt: string;
14
+ eventCount: number;
15
+ uniqueUserCount: number;
16
+ easBuilds: AppEasBuildJson[];
17
+ }
18
+ export interface AppUpdateJson {
19
+ appUpdateId: string;
20
+ firstSeenAt: string;
21
+ eventCount: number;
22
+ uniqueUserCount: number;
23
+ easBuilds: AppEasBuildJson[];
24
+ }
25
+ export interface AppEasBuildJson {
26
+ easBuildId: string;
27
+ firstSeenAt: string;
28
+ eventCount: number;
29
+ uniqueUserCount: number;
30
+ }
31
+ export declare function buildObserveVersionsJson(results: AppVersionsResult[]): AppVersionJson[];
32
+ export declare function buildObserveVersionsTable(results: AppVersionsResult[]): string;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildObserveVersionsJson = buildObserveVersionsJson;
4
+ exports.buildObserveVersionsTable = buildObserveVersionsTable;
5
+ const tslib_1 = require("tslib");
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const platform_1 = require("../platform");
8
+ function formatDate(isoString) {
9
+ const date = new Date(isoString);
10
+ return date.toLocaleDateString('en-US', {
11
+ year: 'numeric',
12
+ month: 'short',
13
+ day: 'numeric',
14
+ });
15
+ }
16
+ function mapEasBuilds(easBuilds) {
17
+ return easBuilds.map(b => ({
18
+ easBuildId: b.easBuildId,
19
+ firstSeenAt: b.firstSeenAt,
20
+ eventCount: b.eventCount,
21
+ uniqueUserCount: b.uniqueUserCount,
22
+ }));
23
+ }
24
+ function mapBuildNumbers(buildNumbers) {
25
+ return buildNumbers.map(bn => ({
26
+ appBuildNumber: bn.appBuildNumber,
27
+ firstSeenAt: bn.firstSeenAt,
28
+ eventCount: bn.eventCount,
29
+ uniqueUserCount: bn.uniqueUserCount,
30
+ easBuilds: mapEasBuilds(bn.easBuilds),
31
+ }));
32
+ }
33
+ function mapUpdates(updates) {
34
+ return updates.map(u => ({
35
+ appUpdateId: u.appUpdateId,
36
+ firstSeenAt: u.firstSeenAt,
37
+ eventCount: u.eventCount,
38
+ uniqueUserCount: u.uniqueUserCount,
39
+ easBuilds: mapEasBuilds(u.easBuilds),
40
+ }));
41
+ }
42
+ function buildObserveVersionsJson(results) {
43
+ const output = [];
44
+ for (const { platform, appVersions } of results) {
45
+ for (const v of appVersions) {
46
+ output.push({
47
+ platform: platform,
48
+ appVersion: v.appVersion,
49
+ firstSeenAt: v.firstSeenAt,
50
+ eventCount: v.eventCount,
51
+ uniqueUserCount: v.uniqueUserCount,
52
+ buildNumbers: mapBuildNumbers(v.buildNumbers),
53
+ updates: mapUpdates(v.updates),
54
+ });
55
+ }
56
+ }
57
+ return output;
58
+ }
59
+ function renderTable(headers, rows) {
60
+ const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map(r => r[i].length)));
61
+ const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(' ');
62
+ const separatorLine = colWidths.map(w => '-'.repeat(w)).join(' ');
63
+ const dataLines = rows.map(row => row.map((cell, i) => cell.padEnd(colWidths[i])).join(' '));
64
+ return [chalk_1.default.bold(headerLine), separatorLine, ...dataLines].join('\n');
65
+ }
66
+ function buildObserveVersionsTable(results) {
67
+ const hasAnyVersions = results.some(r => r.appVersions.length > 0);
68
+ if (!hasAnyVersions) {
69
+ return chalk_1.default.yellow('No app versions found.');
70
+ }
71
+ const headers = ['App Version', 'First Seen', 'Events', 'Users', 'Builds', 'Updates'];
72
+ const sections = [];
73
+ for (const { platform, appVersions } of results) {
74
+ if (appVersions.length === 0) {
75
+ continue;
76
+ }
77
+ if (sections.length > 0) {
78
+ sections.push('');
79
+ }
80
+ sections.push(chalk_1.default.bold(platform_1.appPlatformDisplayNames[platform]));
81
+ const rows = appVersions.map(version => [
82
+ version.appVersion,
83
+ formatDate(version.firstSeenAt),
84
+ String(version.eventCount),
85
+ String(version.uniqueUserCount),
86
+ String(version.buildNumbers.length),
87
+ String(version.updates.length),
88
+ ]);
89
+ sections.push(renderTable(headers, rows));
90
+ }
91
+ return sections.join('\n');
92
+ }
@@ -0,0 +1,4 @@
1
+ export declare const METRIC_ALIASES: Record<string, string>;
2
+ export declare const METRIC_SHORT_NAMES: Record<string, string>;
3
+ export declare function resolveMetricName(input: string): string;
4
+ export declare function getMetricDisplayName(metricName: string): string;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.METRIC_SHORT_NAMES = exports.METRIC_ALIASES = void 0;
4
+ exports.resolveMetricName = resolveMetricName;
5
+ exports.getMetricDisplayName = getMetricDisplayName;
6
+ const errors_1 = require("../commandUtils/errors");
7
+ exports.METRIC_ALIASES = {
8
+ tti: 'expo.app_startup.tti',
9
+ ttr: 'expo.app_startup.ttr',
10
+ cold_launch: 'expo.app_startup.cold_launch_time',
11
+ warm_launch: 'expo.app_startup.warm_launch_time',
12
+ bundle_load: 'expo.app_startup.bundle_load_time',
13
+ };
14
+ const KNOWN_FULL_NAMES = new Set(Object.values(exports.METRIC_ALIASES));
15
+ exports.METRIC_SHORT_NAMES = {
16
+ 'expo.app_startup.cold_launch_time': 'Cold Launch',
17
+ 'expo.app_startup.warm_launch_time': 'Warm Launch',
18
+ 'expo.app_startup.tti': 'TTI',
19
+ 'expo.app_startup.ttr': 'TTR',
20
+ 'expo.app_startup.bundle_load_time': 'Bundle Load',
21
+ };
22
+ function resolveMetricName(input) {
23
+ if (exports.METRIC_ALIASES[input]) {
24
+ return exports.METRIC_ALIASES[input];
25
+ }
26
+ if (KNOWN_FULL_NAMES.has(input) || input.includes('.')) {
27
+ return input;
28
+ }
29
+ throw new errors_1.EasCommandError(`Unknown metric: "${input}". Use a full metric name (e.g. expo.app_startup.tti) or a short alias: ${Object.keys(exports.METRIC_ALIASES).join(', ')}`);
30
+ }
31
+ function getMetricDisplayName(metricName) {
32
+ return exports.METRIC_SHORT_NAMES[metricName] ?? metricName;
33
+ }
@@ -0,0 +1,18 @@
1
+ export declare const DEFAULT_DAYS_BACK = 60;
2
+ export declare function startAndEndTime({ daysBack, start, end, }: {
3
+ daysBack?: number;
4
+ start?: string;
5
+ end?: string;
6
+ }): {
7
+ startTime: string;
8
+ endTime: string;
9
+ };
10
+ export declare function resolveTimeRange(flags: {
11
+ days?: number;
12
+ start?: string;
13
+ end?: string;
14
+ }): {
15
+ daysBack?: number;
16
+ startTime: string;
17
+ endTime: string;
18
+ };