eas-cli 18.10.0 → 18.12.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 (56) hide show
  1. package/README.md +107 -102
  2. package/build/build/runBuildAndSubmit.d.ts +3 -1
  3. package/build/build/runBuildAndSubmit.js +12 -4
  4. package/build/build/utils/repository.js +8 -0
  5. package/build/build/validateLockfile.d.ts +1 -0
  6. package/build/build/validateLockfile.js +37 -0
  7. package/build/commandUtils/buildFlags.d.ts +1 -0
  8. package/build/commandUtils/buildFlags.js +18 -0
  9. package/build/commandUtils/convex.d.ts +1 -0
  10. package/build/commandUtils/convex.js +8 -2
  11. package/build/commands/build/dev.d.ts +1 -0
  12. package/build/commands/build/dev.js +12 -2
  13. package/build/commands/build/run.d.ts +1 -0
  14. package/build/commands/build/run.js +12 -3
  15. package/build/commands/integrations/convex/connect.d.ts +1 -0
  16. package/build/commands/integrations/convex/connect.js +65 -7
  17. package/build/commands/integrations/convex/team/invite.js +6 -1
  18. package/build/commands/observe/events.js +12 -24
  19. package/build/commands/observe/logs.d.ts +29 -0
  20. package/build/commands/observe/logs.js +163 -0
  21. package/build/commands/observe/metrics.js +11 -19
  22. package/build/commands/observe/versions.js +11 -19
  23. package/build/commands/simulator/start.js +85 -92
  24. package/build/commands/simulator/stop.js +1 -1
  25. package/build/graphql/generated.d.ts +290 -10
  26. package/build/graphql/generated.js +32 -3
  27. package/build/graphql/mutations/DeviceRunSessionMutation.d.ts +2 -2
  28. package/build/graphql/mutations/DeviceRunSessionMutation.js +4 -4
  29. package/build/graphql/queries/DeviceRunSessionQuery.js +8 -1
  30. package/build/graphql/queries/ObserveQuery.d.ts +21 -1
  31. package/build/graphql/queries/ObserveQuery.js +80 -0
  32. package/build/graphql/types/ConvexTeamConnection.d.ts +1 -1
  33. package/build/graphql/types/ConvexTeamConnection.js +1 -0
  34. package/build/graphql/types/Observe.d.ts +1 -0
  35. package/build/graphql/types/Observe.js +26 -1
  36. package/build/observe/fetchCustomEvents.d.ts +19 -0
  37. package/build/observe/fetchCustomEvents.js +21 -0
  38. package/build/observe/formatCustomEvents.d.ts +70 -0
  39. package/build/observe/formatCustomEvents.js +140 -0
  40. package/build/observe/formatEvents.js +5 -34
  41. package/build/observe/formatMetrics.js +2 -7
  42. package/build/observe/formatUtils.d.ts +27 -0
  43. package/build/observe/formatUtils.js +64 -0
  44. package/build/observe/formatVersions.js +2 -9
  45. package/build/observe/platforms.d.ts +21 -0
  46. package/build/observe/platforms.js +48 -0
  47. package/build/observe/resolveProjectContext.d.ts +22 -0
  48. package/build/observe/resolveProjectContext.js +21 -0
  49. package/build/run/ios/run.d.ts +2 -1
  50. package/build/run/ios/run.js +6 -2
  51. package/build/run/ios/simulator.d.ts +4 -1
  52. package/build/run/ios/simulator.js +14 -2
  53. package/build/run/run.d.ts +2 -1
  54. package/build/run/run.js +2 -2
  55. package/oclif.manifest.json +568 -375
  56. package/package.json +5 -5
@@ -13,10 +13,17 @@ exports.DeviceRunSessionQuery = {
13
13
  byId(deviceRunSessionId: $deviceRunSessionId) {
14
14
  id
15
15
  status
16
+ type
17
+ remoteConfig {
18
+ __typename
19
+ ... on AgentDeviceRunSessionRemoteConfig {
20
+ url
21
+ token
22
+ }
23
+ }
16
24
  turtleJobRun {
17
25
  id
18
26
  status
19
- logFileUrls
20
27
  }
21
28
  }
22
29
  }
@@ -1,5 +1,5 @@
1
1
  import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
2
- import { AppObserveAppVersion, AppObserveEvent, AppObserveEventsFilter, AppObserveEventsOrderBy, AppObservePlatform, AppObserveTimeSeriesStatistics, PageInfo } from '../generated';
2
+ import { AppObserveAppVersion, AppObserveCustomEvent, AppObserveCustomEventListFilter, AppObserveCustomEventName, AppObserveEvent, AppObserveEventsFilter, AppObserveEventsOrderBy, AppObservePlatform, AppObserveTimeSeriesStatistics, PageInfo } from '../generated';
3
3
  export type AppObserveTimeSeriesResult = {
4
4
  appVersionMarkers: AppObserveAppVersion[];
5
5
  eventCount: number;
@@ -12,6 +12,12 @@ type AppObserveEventsQueryVariables = {
12
12
  after?: string;
13
13
  orderBy?: AppObserveEventsOrderBy;
14
14
  };
15
+ type AppObserveCustomEventListQueryVariables = {
16
+ appId: string;
17
+ filter?: AppObserveCustomEventListFilter;
18
+ first?: number;
19
+ after?: string;
20
+ };
15
21
  export declare const ObserveQuery: {
16
22
  timeSeriesAsync(graphqlClient: ExpoGraphqlClient, { appId, metricName, platform, startTime, endTime, }: {
17
23
  appId: string;
@@ -31,5 +37,19 @@ export declare const ObserveQuery: {
31
37
  events: AppObserveEvent[];
32
38
  pageInfo: PageInfo;
33
39
  }>;
40
+ customEventListAsync(graphqlClient: ExpoGraphqlClient, variables: AppObserveCustomEventListQueryVariables): Promise<{
41
+ events: AppObserveCustomEvent[];
42
+ pageInfo: PageInfo;
43
+ }>;
44
+ customEventNamesAsync(graphqlClient: ExpoGraphqlClient, { appId, startTime, endTime, platform, environment, }: {
45
+ appId: string;
46
+ startTime: string;
47
+ endTime: string;
48
+ platform?: AppObservePlatform;
49
+ environment?: string;
50
+ }): Promise<{
51
+ names: AppObserveCustomEventName[];
52
+ isTruncated: boolean;
53
+ }>;
34
54
  };
35
55
  export {};
@@ -106,4 +106,84 @@ exports.ObserveQuery = {
106
106
  pageInfo,
107
107
  };
108
108
  },
109
+ async customEventListAsync(graphqlClient, variables) {
110
+ const data = await (0, client_1.withErrorHandlingAsync)(graphqlClient
111
+ .query((0, graphql_tag_1.default) `
112
+ query AppObserveCustomEventList(
113
+ $appId: String!
114
+ $filter: AppObserveCustomEventListFilter
115
+ $first: Int
116
+ $after: String
117
+ ) {
118
+ app {
119
+ byId(appId: $appId) {
120
+ id
121
+ observe {
122
+ customEventList(filter: $filter, first: $first, after: $after) {
123
+ pageInfo {
124
+ hasNextPage
125
+ hasPreviousPage
126
+ endCursor
127
+ }
128
+ edges {
129
+ cursor
130
+ node {
131
+ id
132
+ ...AppObserveCustomEventFragment
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ }
140
+ ${(0, graphql_1.print)(Observe_1.AppObserveCustomEventFragmentNode)}
141
+ `, variables)
142
+ .toPromise());
143
+ const { edges, pageInfo } = data.app.byId.observe.customEventList;
144
+ return {
145
+ events: edges.map(edge => edge.node),
146
+ pageInfo,
147
+ };
148
+ },
149
+ async customEventNamesAsync(graphqlClient, { appId, startTime, endTime, platform, environment, }) {
150
+ const data = await (0, client_1.withErrorHandlingAsync)(graphqlClient
151
+ .query((0, graphql_tag_1.default) `
152
+ query AppObserveCustomEventNames(
153
+ $appId: String!
154
+ $startTime: DateTime!
155
+ $endTime: DateTime!
156
+ $platform: AppObservePlatform
157
+ $environment: String
158
+ ) {
159
+ app {
160
+ byId(appId: $appId) {
161
+ id
162
+ observe {
163
+ customEventNames(
164
+ startTime: $startTime
165
+ endTime: $endTime
166
+ platform: $platform
167
+ environment: $environment
168
+ ) {
169
+ isTruncated
170
+ names {
171
+ eventName
172
+ count
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ }
179
+ `, {
180
+ appId,
181
+ startTime,
182
+ endTime,
183
+ ...(platform && { platform }),
184
+ ...(environment && { environment }),
185
+ })
186
+ .toPromise());
187
+ return data.app.byId.observe.customEventNames;
188
+ },
109
189
  };
@@ -1,5 +1,5 @@
1
1
  import { ConvexProject, ConvexTeamConnection, SetupConvexProjectResult } from '../generated';
2
- export type ConvexTeamConnectionData = Pick<ConvexTeamConnection, 'id' | 'convexTeamIdentifier' | 'convexTeamName' | 'convexTeamSlug' | 'createdAt' | 'updatedAt' | 'invitedAt' | 'invitedEmail'>;
2
+ export type ConvexTeamConnectionData = Pick<ConvexTeamConnection, 'id' | 'convexTeamIdentifier' | 'convexTeamName' | 'convexTeamSlug' | 'hasBeenClaimed' | 'createdAt' | 'updatedAt' | 'invitedAt' | 'invitedEmail'>;
3
3
  export type ConvexProjectData = Pick<ConvexProject, 'id' | 'convexProjectIdentifier' | 'convexProjectName' | 'convexProjectSlug' | 'createdAt' | 'updatedAt'> & {
4
4
  convexTeamConnection: ConvexTeamConnectionData;
5
5
  };
@@ -9,6 +9,7 @@ exports.ConvexTeamConnectionFragmentNode = (0, graphql_tag_1.default) `
9
9
  convexTeamIdentifier
10
10
  convexTeamName
11
11
  convexTeamSlug
12
+ hasBeenClaimed
12
13
  createdAt
13
14
  updatedAt
14
15
  invitedAt
@@ -1,3 +1,4 @@
1
1
  export declare const AppObserveTimeSeriesFragmentNode: import("graphql").DocumentNode;
2
+ export declare const AppObserveCustomEventFragmentNode: import("graphql").DocumentNode;
2
3
  export declare const AppObserveEventFragmentNode: import("graphql").DocumentNode;
3
4
  export declare const AppObserveAppVersionFragmentNode: import("graphql").DocumentNode;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AppObserveAppVersionFragmentNode = exports.AppObserveEventFragmentNode = exports.AppObserveTimeSeriesFragmentNode = void 0;
3
+ exports.AppObserveAppVersionFragmentNode = exports.AppObserveEventFragmentNode = exports.AppObserveCustomEventFragmentNode = exports.AppObserveTimeSeriesFragmentNode = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
6
6
  exports.AppObserveTimeSeriesFragmentNode = (0, graphql_tag_1.default) `
@@ -20,6 +20,31 @@ exports.AppObserveTimeSeriesFragmentNode = (0, graphql_tag_1.default) `
20
20
  }
21
21
  }
22
22
  `;
23
+ exports.AppObserveCustomEventFragmentNode = (0, graphql_tag_1.default) `
24
+ fragment AppObserveCustomEventFragment on AppObserveCustomEvent {
25
+ id
26
+ eventName
27
+ timestamp
28
+ sessionId
29
+ severityNumber
30
+ severityText
31
+ properties {
32
+ key
33
+ value
34
+ type
35
+ }
36
+ appVersion
37
+ appBuildNumber
38
+ appUpdateId
39
+ appEasBuildId
40
+ deviceOs
41
+ deviceOsVersion
42
+ deviceModel
43
+ environment
44
+ easClientId
45
+ countryCode
46
+ }
47
+ `;
23
48
  exports.AppObserveEventFragmentNode = (0, graphql_tag_1.default) `
24
49
  fragment AppObserveEventFragment on AppObserveEvent {
25
50
  id
@@ -0,0 +1,19 @@
1
+ import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
2
+ import { AppObserveCustomEvent, AppObservePlatform, PageInfo } from '../graphql/generated';
3
+ interface FetchCustomEventsOptions {
4
+ eventName?: string;
5
+ limit: number;
6
+ after?: string;
7
+ startTime: string;
8
+ endTime: string;
9
+ platform?: AppObservePlatform;
10
+ appVersion?: string;
11
+ updateId?: string;
12
+ sessionId?: string;
13
+ }
14
+ interface FetchCustomEventsResult {
15
+ events: AppObserveCustomEvent[];
16
+ pageInfo: PageInfo;
17
+ }
18
+ export declare function fetchObserveCustomEventsAsync(graphqlClient: ExpoGraphqlClient, appId: string, options: FetchCustomEventsOptions): Promise<FetchCustomEventsResult>;
19
+ export {};
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchObserveCustomEventsAsync = fetchObserveCustomEventsAsync;
4
+ const ObserveQuery_1 = require("../graphql/queries/ObserveQuery");
5
+ async function fetchObserveCustomEventsAsync(graphqlClient, appId, options) {
6
+ const filter = {
7
+ startTime: options.startTime,
8
+ endTime: options.endTime,
9
+ ...(options.eventName && { eventName: options.eventName }),
10
+ ...(options.platform && { platform: options.platform }),
11
+ ...(options.appVersion && { appVersion: options.appVersion }),
12
+ ...(options.updateId && { appUpdateId: options.updateId }),
13
+ ...(options.sessionId && { sessionId: options.sessionId }),
14
+ };
15
+ return await ObserveQuery_1.ObserveQuery.customEventListAsync(graphqlClient, {
16
+ appId,
17
+ filter,
18
+ first: options.limit,
19
+ ...(options.after && { after: options.after }),
20
+ });
21
+ }
@@ -0,0 +1,70 @@
1
+ import { AppObserveCustomEvent, AppObserveCustomEventName, PageInfo } from '../graphql/generated';
2
+ export interface ObserveCustomEventPropertyJson {
3
+ key: string;
4
+ value: string;
5
+ type: string;
6
+ }
7
+ export interface ObserveCustomEventJson {
8
+ id: string;
9
+ eventName: string;
10
+ timestamp: string;
11
+ sessionId: string | null;
12
+ severityNumber: number | null;
13
+ severityText: string | null;
14
+ properties: ObserveCustomEventPropertyJson[];
15
+ appVersion: string;
16
+ appBuildNumber: string;
17
+ appUpdateId: string | null;
18
+ appEasBuildId: string | null;
19
+ deviceModel: string;
20
+ deviceOs: string;
21
+ deviceOsVersion: string;
22
+ countryCode: string | null;
23
+ environment: string | null;
24
+ easClientId: string;
25
+ }
26
+ export interface BuildCustomEventsTableOptions {
27
+ eventName?: string;
28
+ daysBack?: number;
29
+ startTime?: string;
30
+ endTime?: string;
31
+ totalEventCount?: number;
32
+ }
33
+ export declare function buildObserveCustomEventsTable(events: AppObserveCustomEvent[], pageInfo: PageInfo, options?: BuildCustomEventsTableOptions): string;
34
+ export declare function buildObserveCustomEventsJson(events: AppObserveCustomEvent[], pageInfo: PageInfo): {
35
+ events: ObserveCustomEventJson[];
36
+ pageInfo: {
37
+ hasNextPage: boolean;
38
+ endCursor: string | null;
39
+ };
40
+ };
41
+ export interface BuildEmptyCustomEventsWithSuggestionsOptions {
42
+ daysBack?: number;
43
+ startTime?: string;
44
+ endTime?: string;
45
+ isTruncated?: boolean;
46
+ }
47
+ export declare function buildObserveCustomEventsEmptyWithSuggestionsTable(eventName: string, names: AppObserveCustomEventName[], options?: BuildEmptyCustomEventsWithSuggestionsOptions): string;
48
+ export declare function buildObserveCustomEventsEmptyWithSuggestionsJson(eventName: string, names: AppObserveCustomEventName[], isTruncated: boolean): {
49
+ filteredEventName: string;
50
+ events: [];
51
+ availableEventNames: Array<{
52
+ eventName: string;
53
+ count: number;
54
+ }>;
55
+ availableEventNamesIsTruncated: boolean;
56
+ };
57
+ export interface BuildCustomEventNamesTableOptions {
58
+ daysBack?: number;
59
+ startTime?: string;
60
+ endTime?: string;
61
+ isTruncated?: boolean;
62
+ }
63
+ export declare function buildObserveCustomEventNamesTable(names: AppObserveCustomEventName[], options?: BuildCustomEventNamesTableOptions): string;
64
+ export declare function buildObserveCustomEventNamesJson(names: AppObserveCustomEventName[], isTruncated: boolean): {
65
+ names: Array<{
66
+ eventName: string;
67
+ count: number;
68
+ }>;
69
+ isTruncated: boolean;
70
+ };
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildObserveCustomEventsTable = buildObserveCustomEventsTable;
4
+ exports.buildObserveCustomEventsJson = buildObserveCustomEventsJson;
5
+ exports.buildObserveCustomEventsEmptyWithSuggestionsTable = buildObserveCustomEventsEmptyWithSuggestionsTable;
6
+ exports.buildObserveCustomEventsEmptyWithSuggestionsJson = buildObserveCustomEventsEmptyWithSuggestionsJson;
7
+ exports.buildObserveCustomEventNamesTable = buildObserveCustomEventNamesTable;
8
+ exports.buildObserveCustomEventNamesJson = buildObserveCustomEventNamesJson;
9
+ const tslib_1 = require("tslib");
10
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
11
+ const renderTextTable_1 = tslib_1.__importDefault(require("../utils/renderTextTable"));
12
+ const formatUtils_1 = require("./formatUtils");
13
+ function formatSeverity(event) {
14
+ if (event.severityText) {
15
+ return event.severityText;
16
+ }
17
+ if (event.severityNumber != null) {
18
+ return String(event.severityNumber);
19
+ }
20
+ return '-';
21
+ }
22
+ function buildObserveCustomEventsTable(events, pageInfo, options) {
23
+ if (events.length === 0) {
24
+ return chalk_1.default.yellow('No custom events found.');
25
+ }
26
+ const showEventName = !options?.eventName;
27
+ const hasSeverity = events.some(e => e.severityText != null || e.severityNumber != null);
28
+ const headers = [
29
+ 'Timestamp',
30
+ ...(showEventName ? ['Event'] : []),
31
+ ...(hasSeverity ? ['Severity'] : []),
32
+ 'App Version',
33
+ 'Platform',
34
+ 'Device',
35
+ 'Country',
36
+ ];
37
+ const rows = events.map(event => [
38
+ (0, formatUtils_1.formatLogTimestamp)(event.timestamp),
39
+ ...(showEventName ? [event.eventName] : []),
40
+ ...(hasSeverity ? [formatSeverity(event)] : []),
41
+ `${event.appVersion} (${event.appBuildNumber})`,
42
+ `${event.deviceOs} ${event.deviceOsVersion}`,
43
+ event.deviceModel,
44
+ event.countryCode ?? '-',
45
+ ]);
46
+ const lines = [];
47
+ if (options) {
48
+ const timeDesc = (0, formatUtils_1.buildTimeRangeDescription)(options);
49
+ const totalDesc = options.totalEventCount != null
50
+ ? ` — ${options.totalEventCount.toLocaleString()} total events`
51
+ : '';
52
+ const subject = options.eventName ? `${options.eventName} events` : 'Custom events';
53
+ lines.push(chalk_1.default.bold(`${subject} ${timeDesc}${totalDesc}`.trim()), '');
54
+ }
55
+ lines.push((0, renderTextTable_1.default)(headers, rows));
56
+ if (pageInfo.hasNextPage && pageInfo.endCursor) {
57
+ lines.push('', `Next page: --after ${pageInfo.endCursor}`);
58
+ }
59
+ return lines.join('\n');
60
+ }
61
+ function buildObserveCustomEventsJson(events, pageInfo) {
62
+ return {
63
+ events: events.map(event => ({
64
+ id: event.id,
65
+ eventName: event.eventName,
66
+ timestamp: event.timestamp,
67
+ sessionId: event.sessionId ?? null,
68
+ severityNumber: event.severityNumber ?? null,
69
+ severityText: event.severityText ?? null,
70
+ properties: event.properties.map(p => ({
71
+ key: p.key,
72
+ value: p.value,
73
+ type: p.type,
74
+ })),
75
+ appVersion: event.appVersion,
76
+ appBuildNumber: event.appBuildNumber,
77
+ appUpdateId: event.appUpdateId ?? null,
78
+ appEasBuildId: event.appEasBuildId ?? null,
79
+ deviceModel: event.deviceModel,
80
+ deviceOs: event.deviceOs,
81
+ deviceOsVersion: event.deviceOsVersion,
82
+ countryCode: event.countryCode ?? null,
83
+ environment: event.environment ?? null,
84
+ easClientId: event.easClientId,
85
+ })),
86
+ pageInfo: {
87
+ hasNextPage: pageInfo.hasNextPage,
88
+ endCursor: pageInfo.endCursor ?? null,
89
+ },
90
+ };
91
+ }
92
+ function buildObserveCustomEventsEmptyWithSuggestionsTable(eventName, names, options) {
93
+ const lines = [];
94
+ const timeDesc = options ? (0, formatUtils_1.buildTimeRangeDescription)(options) : '';
95
+ lines.push(chalk_1.default.yellow(`No events found matching "${eventName}" ${timeDesc}.`.trim()));
96
+ if (names.length === 0) {
97
+ lines.push('', chalk_1.default.yellow('No custom event names found in this time range.'));
98
+ return lines.join('\n');
99
+ }
100
+ lines.push('', 'Available event names in this time range:', '');
101
+ const headers = ['Event Name', 'Count'];
102
+ const rows = names.map(n => [n.eventName, n.count.toLocaleString()]);
103
+ lines.push((0, renderTextTable_1.default)(headers, rows));
104
+ if (options?.isTruncated) {
105
+ lines.push('', chalk_1.default.yellow('Result is truncated; not all event names are shown.'));
106
+ }
107
+ return lines.join('\n');
108
+ }
109
+ function buildObserveCustomEventsEmptyWithSuggestionsJson(eventName, names, isTruncated) {
110
+ return {
111
+ filteredEventName: eventName,
112
+ events: [],
113
+ availableEventNames: names.map(n => ({ eventName: n.eventName, count: n.count })),
114
+ availableEventNamesIsTruncated: isTruncated,
115
+ };
116
+ }
117
+ function buildObserveCustomEventNamesTable(names, options) {
118
+ if (names.length === 0) {
119
+ return chalk_1.default.yellow('No custom event names found.');
120
+ }
121
+ const headers = ['Event Name', 'Count'];
122
+ const rows = names.map(n => [n.eventName, n.count.toLocaleString()]);
123
+ const lines = [];
124
+ if (options) {
125
+ const timeDesc = (0, formatUtils_1.buildTimeRangeDescription)(options);
126
+ const subject = 'Custom event names';
127
+ lines.push(chalk_1.default.bold(`${subject} ${timeDesc}`.trim()), '');
128
+ }
129
+ lines.push((0, renderTextTable_1.default)(headers, rows));
130
+ if (options?.isTruncated) {
131
+ lines.push('', chalk_1.default.yellow('Result is truncated; not all event names are shown.'));
132
+ }
133
+ return lines.join('\n');
134
+ }
135
+ function buildObserveCustomEventNamesJson(names, isTruncated) {
136
+ return {
137
+ names: names.map(n => ({ eventName: n.eventName, count: n.count })),
138
+ isTruncated,
139
+ };
140
+ }
@@ -4,25 +4,9 @@ exports.buildObserveEventsTable = buildObserveEventsTable;
4
4
  exports.buildObserveEventsJson = buildObserveEventsJson;
5
5
  const tslib_1 = require("tslib");
6
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const renderTextTable_1 = tslib_1.__importDefault(require("../utils/renderTextTable"));
8
+ const formatUtils_1 = require("./formatUtils");
7
9
  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
10
  function resolveCustomParams(event) {
27
11
  return event.customParams ?? null;
28
12
  }
@@ -47,31 +31,18 @@ function buildObserveEventsTable(events, pageInfo, options) {
47
31
  `${event.deviceOs} ${event.deviceOsVersion}`,
48
32
  event.deviceModel,
49
33
  event.countryCode ?? '-',
50
- formatTimestamp(event.timestamp),
34
+ (0, formatUtils_1.formatTimestamp)(event.timestamp),
51
35
  ]);
52
- const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map(r => r[i].length)));
53
- const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(' ');
54
- const separatorLine = colWidths.map(w => '-'.repeat(w)).join(' ');
55
- const dataLines = rows.map(row => row.map((cell, i) => cell.padEnd(colWidths[i])).join(' '));
56
36
  const lines = [];
57
37
  if (options) {
58
38
  const metricDisplay = (0, metricNames_1.getMetricDisplayName)(options.metricName);
59
- let timeDesc;
60
- if (options.daysBack) {
61
- timeDesc = `for the last ${options.daysBack} days`;
62
- }
63
- else if (options.startTime && options.endTime) {
64
- timeDesc = `from ${formatDate(options.startTime)} to ${formatDate(options.endTime)}`;
65
- }
66
- else {
67
- timeDesc = '';
68
- }
39
+ const timeDesc = (0, formatUtils_1.buildTimeRangeDescription)(options);
69
40
  const totalDesc = options.totalEventCount != null
70
41
  ? ` — ${options.totalEventCount.toLocaleString()} total events`
71
42
  : '';
72
43
  lines.push(chalk_1.default.bold(`${metricDisplay} events ${timeDesc}${totalDesc}`.trim()), '');
73
44
  }
74
- lines.push(chalk_1.default.bold(headerLine), separatorLine, ...dataLines);
45
+ lines.push((0, renderTextTable_1.default)(headers, rows));
75
46
  if (pageInfo.hasNextPage && pageInfo.endCursor) {
76
47
  lines.push('', `Next page: --after ${pageInfo.endCursor}`);
77
48
  }
@@ -10,6 +10,7 @@ const chalk_1 = tslib_1.__importDefault(require("chalk"));
10
10
  const errors_1 = require("../commandUtils/errors");
11
11
  const platform_1 = require("../platform");
12
12
  const renderTextTable_1 = tslib_1.__importDefault(require("../utils/renderTextTable"));
13
+ const formatUtils_1 = require("./formatUtils");
13
14
  const metricNames_1 = require("./metricNames");
14
15
  exports.STAT_ALIASES = {
15
16
  min: 'min',
@@ -105,12 +106,6 @@ function buildObserveMetricsJson(metricsMap, metricNames, stats, totalEventCount
105
106
  function buildStatsDescription(displayStats) {
106
107
  return displayStats.map(s => exports.STAT_DISPLAY_NAMES[s]).join(', ');
107
108
  }
108
- function buildTimeRangeDescription(daysBack) {
109
- if (daysBack) {
110
- return `for the last ${daysBack} days`;
111
- }
112
- return '';
113
- }
114
109
  function buildObserveMetricsTable(metricsMap, metricNames, stats, options) {
115
110
  const { versions: results } = buildObserveMetricsJson(metricsMap, metricNames, stats);
116
111
  if (results.length === 0) {
@@ -120,7 +115,7 @@ function buildObserveMetricsTable(metricsMap, metricNames, stats, options) {
120
115
  const hasEventCount = stats.includes('eventCount');
121
116
  // Build summary header
122
117
  const statsDesc = displayStats.length > 0 ? buildStatsDescription(displayStats) : 'Event count';
123
- const timeDesc = buildTimeRangeDescription(options?.daysBack);
118
+ const timeDesc = (0, formatUtils_1.buildTimeRangeDescription)({ daysBack: options?.daysBack });
124
119
  const countSuffix = hasEventCount && displayStats.length > 0 ? ' (event count)' : '';
125
120
  const summaryLine = `${statsDesc} values${countSuffix}${timeDesc ? ` ${timeDesc}` : ''}`;
126
121
  // Group results by platform
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Format an ISO timestamp for display in event tables (minute precision).
3
+ * Uses the runtime's default locale.
4
+ */
5
+ export declare function formatTimestamp(isoString: string): string;
6
+ /**
7
+ * Format an ISO timestamp for display in log tables (millisecond precision).
8
+ * Use this instead of formatTimestamp when the table represents individual
9
+ * log entries where sub-minute resolution matters. Uses the runtime's
10
+ * default locale.
11
+ */
12
+ export declare function formatLogTimestamp(isoString: string): string;
13
+ /**
14
+ * Format an ISO timestamp for display as a date only (no time). Uses the
15
+ * runtime's default locale.
16
+ */
17
+ export declare function formatDate(isoString: string): string;
18
+ /**
19
+ * Build the time-range fragment used in summary headers, e.g.
20
+ * "for the last 7 days" or "from Jan 1, 2025 to Feb 1, 2025".
21
+ * Returns an empty string when no range information is provided.
22
+ */
23
+ export declare function buildTimeRangeDescription(options: {
24
+ daysBack?: number;
25
+ startTime?: string;
26
+ endTime?: string;
27
+ }): string;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatTimestamp = formatTimestamp;
4
+ exports.formatLogTimestamp = formatLogTimestamp;
5
+ exports.formatDate = formatDate;
6
+ exports.buildTimeRangeDescription = buildTimeRangeDescription;
7
+ /**
8
+ * Format an ISO timestamp for display in event tables (minute precision).
9
+ * Uses the runtime's default locale.
10
+ */
11
+ function formatTimestamp(isoString) {
12
+ const date = new Date(isoString);
13
+ return date.toLocaleDateString(undefined, {
14
+ year: 'numeric',
15
+ month: 'short',
16
+ day: 'numeric',
17
+ hour: '2-digit',
18
+ minute: '2-digit',
19
+ });
20
+ }
21
+ /**
22
+ * Format an ISO timestamp for display in log tables (millisecond precision).
23
+ * Use this instead of formatTimestamp when the table represents individual
24
+ * log entries where sub-minute resolution matters. Uses the runtime's
25
+ * default locale.
26
+ */
27
+ function formatLogTimestamp(isoString) {
28
+ const date = new Date(isoString);
29
+ return date.toLocaleDateString(undefined, {
30
+ year: 'numeric',
31
+ month: 'short',
32
+ day: 'numeric',
33
+ hour: '2-digit',
34
+ minute: '2-digit',
35
+ second: '2-digit',
36
+ fractionalSecondDigits: 3,
37
+ });
38
+ }
39
+ /**
40
+ * Format an ISO timestamp for display as a date only (no time). Uses the
41
+ * runtime's default locale.
42
+ */
43
+ function formatDate(isoString) {
44
+ const date = new Date(isoString);
45
+ return date.toLocaleDateString(undefined, {
46
+ year: 'numeric',
47
+ month: 'short',
48
+ day: 'numeric',
49
+ });
50
+ }
51
+ /**
52
+ * Build the time-range fragment used in summary headers, e.g.
53
+ * "for the last 7 days" or "from Jan 1, 2025 to Feb 1, 2025".
54
+ * Returns an empty string when no range information is provided.
55
+ */
56
+ function buildTimeRangeDescription(options) {
57
+ if (options.daysBack) {
58
+ return `for the last ${options.daysBack} days`;
59
+ }
60
+ if (options.startTime && options.endTime) {
61
+ return `from ${formatDate(options.startTime)} to ${formatDate(options.endTime)}`;
62
+ }
63
+ return '';
64
+ }
@@ -6,14 +6,7 @@ const tslib_1 = require("tslib");
6
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
7
  const platform_1 = require("../platform");
8
8
  const renderTextTable_1 = tslib_1.__importDefault(require("../utils/renderTextTable"));
9
- function formatDate(isoString) {
10
- const date = new Date(isoString);
11
- return date.toLocaleDateString('en-US', {
12
- year: 'numeric',
13
- month: 'short',
14
- day: 'numeric',
15
- });
16
- }
9
+ const formatUtils_1 = require("./formatUtils");
17
10
  function mapEasBuilds(easBuilds) {
18
11
  return easBuilds.map(b => ({
19
12
  easBuildId: b.easBuildId,
@@ -74,7 +67,7 @@ function buildObserveVersionsTable(results) {
74
67
  sections.push(chalk_1.default.bold(platform_1.appPlatformDisplayNames[platform]));
75
68
  const rows = appVersions.map(version => [
76
69
  version.appVersion,
77
- formatDate(version.firstSeenAt),
70
+ (0, formatUtils_1.formatDate)(version.firstSeenAt),
78
71
  String(version.eventCount),
79
72
  String(version.uniqueUserCount),
80
73
  String(version.buildNumbers.length),