@testrelic/maestro-analytics 1.1.0 → 1.2.0-next.54

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.
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { TestStatus, CloudReporterOptions, ReportMode, CloudConfig, TimelineEntry, Summary, TestRunReport, AuthMode, GitMetadata, TimelineStep, CIMetadata } from '@testrelic/core';
1
+ import { TestStatus, CloudReporterOptions, ReportMode, CloudConfig, ApiCallRecord, TimelineEntry, Summary, TestRunReport, AuthMode, GitMetadata, TimelineStep, CIMetadata } from '@testrelic/core';
2
2
  export { ActionCategory, ActionStep, ArtifactRunManifest, AuthMode, AuthState, CIMetadata, CIProvider, CloudConfig, CloudReporterOptions, ConsoleLogEntry, ConsoleLogLevel, FailureDiagnostic, GitMetadata, MergeOptions, QueueEntry, ReportMode, StreamingReportSummary, Summary, TestArtifacts, TestDetailData, TestIndexEntry, TestResult, TestRunReport, TestStatus, TimelineEntry, TimelineStep, UploadStrategy } from '@testrelic/core';
3
3
  export { mergeReports, mergeReportsFromDirectory } from './merge.cjs';
4
4
 
@@ -11,6 +11,55 @@ export { mergeReports, mergeReportsFromDirectory } from './merge.cjs';
11
11
  */
12
12
 
13
13
  type MaestroPlatform = 'android' | 'ios' | 'web' | 'unknown';
14
+ /**
15
+ * Network capture configuration. See `network-proxy.ts` and `parsers/har-parser.ts`.
16
+ *
17
+ * Maestro is a black-box UI driver — it can't intercept HTTP from inside the
18
+ * mobile app the way Playwright can. Two strategies are supported:
19
+ *
20
+ * - `mitmproxy` (auto-spawn): SDK launches `mitmdump` with a bundled addon
21
+ * that writes redacted ApiCallRecord JSONL. Requires `mitmdump` on PATH
22
+ * and a configured proxy / CA on the device.
23
+ * - `har` (user-supplied): User produces a HAR file with their own proxy
24
+ * (Charles, Proxyman, mitmweb, etc.) and points the SDK at it.
25
+ */
26
+ interface NetworkCaptureConfig {
27
+ /** When true, attempt to spawn mitmproxy and inject proxy settings into the device. Default: false. */
28
+ readonly enabled?: boolean;
29
+ /** Proxy listen port. Default: 8080. */
30
+ readonly proxyPort?: number;
31
+ /** Proxy listen host. Default: 127.0.0.1. */
32
+ readonly proxyHost?: string;
33
+ /** Path to a pre-generated HAR file to import instead of spawning a proxy. */
34
+ readonly harPath?: string;
35
+ /** Override the directory where mitmproxy writes its JSONL output. Default: `<outputDir>/network`. */
36
+ readonly outputDir?: string;
37
+ /** Skip auto-installing the mitmproxy CA on the device. Useful when the user pre-installed it. */
38
+ readonly skipCertInstall?: boolean;
39
+ /** Only capture calls whose URL matches one of these patterns (string substring or RegExp). */
40
+ readonly includeUrls?: (string | RegExp)[];
41
+ /** Exclude calls matching these patterns. Takes precedence over `includeUrls`. */
42
+ readonly excludeUrls?: (string | RegExp)[];
43
+ /** Header names whose values are replaced with "[REDACTED]". Case-insensitive. */
44
+ readonly redactHeaders?: string[];
45
+ /** Body field names whose values are replaced with "[REDACTED]" at any JSON depth. */
46
+ readonly redactBodyFields?: string[];
47
+ /** Maximum body size to capture (bytes). Larger bodies are truncated. Default: 1 MiB. */
48
+ readonly maxBodyBytes?: number;
49
+ }
50
+ interface ResolvedNetworkCaptureConfig {
51
+ readonly enabled: boolean;
52
+ readonly proxyPort: number;
53
+ readonly proxyHost: string;
54
+ readonly harPath: string | null;
55
+ readonly outputDir: string | null;
56
+ readonly skipCertInstall: boolean;
57
+ readonly includeUrls: readonly (string | RegExp)[];
58
+ readonly excludeUrls: readonly (string | RegExp)[];
59
+ readonly redactHeaders: readonly string[];
60
+ readonly redactBodyFields: readonly string[];
61
+ readonly maxBodyBytes: number;
62
+ }
14
63
  interface MaestroReporterConfig {
15
64
  readonly outputPath?: string;
16
65
  readonly htmlReportPath?: string;
@@ -26,6 +75,7 @@ interface MaestroReporterConfig {
26
75
  readonly cloud?: CloudReporterOptions;
27
76
  readonly reportMode?: ReportMode;
28
77
  readonly quiet?: boolean;
78
+ readonly network?: NetworkCaptureConfig;
29
79
  }
30
80
  interface ResolvedMaestroConfig {
31
81
  readonly outputPath: string;
@@ -42,6 +92,7 @@ interface ResolvedMaestroConfig {
42
92
  readonly cloud: CloudConfig | null;
43
93
  readonly reportMode: ReportMode;
44
94
  readonly quiet: boolean;
95
+ readonly network: ResolvedNetworkCaptureConfig;
45
96
  }
46
97
  interface JUnitProperty {
47
98
  readonly name: string;
@@ -87,7 +138,35 @@ interface MaestroCommandStep {
87
138
  readonly timestamp: string;
88
139
  readonly selector?: string;
89
140
  readonly error?: string;
141
+ /**
142
+ * Raw passthrough of Maestro's metadata block (status / timestamp / duration / sequenceNumber).
143
+ * Kept separate from `details` because consumers like report-orchestrator depend on
144
+ * specific Maestro-internal keys (e.g. `metadata.path` for `startRecording`).
145
+ */
90
146
  readonly metadata?: Record<string, unknown>;
147
+ /**
148
+ * Extracted human-relevant command parameters surfaced in the timeline.
149
+ * Examples:
150
+ * inputText → { text: "<typed value>" }
151
+ * swipe → { direction: "UP", start: "0,0", end: "0,500", duration: 300 }
152
+ * setLocation → { latitude: 37.7749, longitude: -122.4194 }
153
+ * pressKey → { key: "BACK" }
154
+ * assertWithAI → { assertion: "<prompt>" }
155
+ * runScript → { path: "scripts/foo.js" } or { script: "<truncated source>" }
156
+ *
157
+ * Values are post-redaction (see api-redactor / details-redactor).
158
+ */
159
+ readonly details?: Record<string, unknown>;
160
+ /**
161
+ * Sort tiebreaker when two commands share a timestamp (Maestro emits sub-ms timing
162
+ * for fast back-to-back commands). Source: Maestro's metadata.sequenceNumber.
163
+ */
164
+ readonly sequenceNumber?: number;
165
+ /**
166
+ * Sub-commands when this step wraps a flow-control block (`retry`, `repeat`, `runFlow`).
167
+ * Empty / undefined for leaf commands.
168
+ */
169
+ readonly children?: MaestroCommandStep[];
91
170
  }
92
171
  interface MaestroFlowMetadata {
93
172
  readonly appId: string | null;
@@ -169,6 +248,7 @@ interface MaestroTestOptions {
169
248
  readonly env?: Record<string, string>;
170
249
  readonly quiet?: boolean;
171
250
  readonly maestroArgs?: string[];
251
+ readonly network?: NetworkCaptureConfig;
172
252
  }
173
253
  interface MaestroReportOptions {
174
254
  readonly junitPath?: string;
@@ -229,8 +309,16 @@ declare function discoverFlowFiles(dir: string): string[];
229
309
  * Also detects device/platform metadata from log content.
230
310
  */
231
311
 
232
- declare function parseLogContent(content: string): MaestroLogEntry[];
233
- declare function parseLogFile(filePath: string): MaestroLogEntry[];
312
+ /**
313
+ * @param defaultDate Either an ISO date string (`YYYY-MM-DD`) or a Date object.
314
+ * Used to anchor log lines that only carry a wall-clock time (`HH:MM:SS`).
315
+ * Callers should pass the log file's mtime as a Date so the local-zone
316
+ * conversion produces a correct UTC ISO timestamp for log entries. When a
317
+ * plain string is passed, the timestamp is constructed without TZ-shift
318
+ * compensation (legacy behaviour — may be off by the local UTC offset).
319
+ */
320
+ declare function parseLogContent(content: string, defaultDate?: string | Date): MaestroLogEntry[];
321
+ declare function parseLogFile(filePath: string, defaultDate?: string | Date): MaestroLogEntry[];
234
322
  declare function detectPlatformFromLogs(entries: MaestroLogEntry[]): MaestroPlatform;
235
323
  declare function discoverLogFiles(dir: string): string[];
236
324
 
@@ -261,8 +349,13 @@ declare function getArtifactStats(artifacts: CollectedArtifacts): Record<string,
261
349
  * Maps Maestro data to the @testrelic/core TimelineEntry shape.
262
350
  */
263
351
 
264
- declare function buildTimelineEntry(flow: MaestroFlowResult): TimelineEntry;
265
- declare function buildTimeline(flows: MaestroFlowResult[]): TimelineEntry[];
352
+ /**
353
+ * Build a TimelineEntry for a single flow. Optionally accepts an
354
+ * `apiCallsByFlow` map keyed by `testId` (`${flowFile}::${flowName}`) so the
355
+ * orchestrator can plumb network capture results in without re-shaping flows.
356
+ */
357
+ declare function buildTimelineEntry(flow: MaestroFlowResult, apiCallsByFlow?: ReadonlyMap<string, readonly ApiCallRecord[]>): TimelineEntry;
358
+ declare function buildTimeline(flows: MaestroFlowResult[], apiCallsByFlow?: ReadonlyMap<string, readonly ApiCallRecord[]>): TimelineEntry[];
266
359
 
267
360
  /**
268
361
  * Computes Summary stats from TimelineEntry arrays.
@@ -311,9 +404,15 @@ interface MaestroRunResult {
311
404
  readonly debugOutputDir: string;
312
405
  readonly stdout: string;
313
406
  readonly stderr: string;
407
+ /** Directory where the network proxy wrote JSONL records (null when capture was disabled or failed to start). */
408
+ readonly networkJsonlDir: string | null;
314
409
  }
315
410
  declare function buildMaestroArgs(options: MaestroTestOptions, tempDir: string): string[];
316
- declare function runMaestro(options: MaestroTestOptions): Promise<MaestroRunResult>;
411
+ interface RunMaestroExtras {
412
+ /** Optional network-capture config. When `enabled`, mitmproxy is spawned and the device is configured to route through it. */
413
+ readonly network?: ResolvedNetworkCaptureConfig;
414
+ }
415
+ declare function runMaestro(options: MaestroTestOptions, extras?: RunMaestroExtras): Promise<MaestroRunResult>;
317
416
 
318
417
  /**
319
418
  * Report orchestrator — ties all parsers together to produce
@@ -335,6 +434,12 @@ interface OrchestratorInput {
335
434
  * rely on heuristic detection that may return 'unknown'.
336
435
  */
337
436
  readonly platform?: MaestroPlatform;
437
+ /**
438
+ * Directory containing JSONL files emitted by `network-proxy.ts` (mitmproxy
439
+ * addon). When omitted, the orchestrator still picks up `config.network.harPath`
440
+ * if set. Unrelated to Maestro's own debug output.
441
+ */
442
+ readonly networkJsonlDir?: string | null;
338
443
  }
339
444
  interface OrchestratorResult {
340
445
  readonly report: TestRunReport;
@@ -419,6 +524,13 @@ interface TestResultForUpload {
419
524
  readonly timestamp: string;
420
525
  readonly source?: string | null;
421
526
  }>;
527
+ /**
528
+ * Per-flow captured API calls. The cloud platform's `normalizeApiCall` (in
529
+ * `server/src/utils/artifact-normalizer.ts`) accepts this shape directly and
530
+ * stores it as `network.json`. Surfaced in the Test Detail Network panel and
531
+ * via Ask AI's `query_network_logs` tool — works for free, no cloud changes.
532
+ */
533
+ readonly apiCalls?: readonly ApiCallRecord[];
422
534
  }
423
535
  interface RunUploadPayload {
424
536
  readonly runId: string;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { TestStatus, CloudReporterOptions, ReportMode, CloudConfig, TimelineEntry, Summary, TestRunReport, AuthMode, GitMetadata, TimelineStep, CIMetadata } from '@testrelic/core';
1
+ import { TestStatus, CloudReporterOptions, ReportMode, CloudConfig, ApiCallRecord, TimelineEntry, Summary, TestRunReport, AuthMode, GitMetadata, TimelineStep, CIMetadata } from '@testrelic/core';
2
2
  export { ActionCategory, ActionStep, ArtifactRunManifest, AuthMode, AuthState, CIMetadata, CIProvider, CloudConfig, CloudReporterOptions, ConsoleLogEntry, ConsoleLogLevel, FailureDiagnostic, GitMetadata, MergeOptions, QueueEntry, ReportMode, StreamingReportSummary, Summary, TestArtifacts, TestDetailData, TestIndexEntry, TestResult, TestRunReport, TestStatus, TimelineEntry, TimelineStep, UploadStrategy } from '@testrelic/core';
3
3
  export { mergeReports, mergeReportsFromDirectory } from './merge.js';
4
4
 
@@ -11,6 +11,55 @@ export { mergeReports, mergeReportsFromDirectory } from './merge.js';
11
11
  */
12
12
 
13
13
  type MaestroPlatform = 'android' | 'ios' | 'web' | 'unknown';
14
+ /**
15
+ * Network capture configuration. See `network-proxy.ts` and `parsers/har-parser.ts`.
16
+ *
17
+ * Maestro is a black-box UI driver — it can't intercept HTTP from inside the
18
+ * mobile app the way Playwright can. Two strategies are supported:
19
+ *
20
+ * - `mitmproxy` (auto-spawn): SDK launches `mitmdump` with a bundled addon
21
+ * that writes redacted ApiCallRecord JSONL. Requires `mitmdump` on PATH
22
+ * and a configured proxy / CA on the device.
23
+ * - `har` (user-supplied): User produces a HAR file with their own proxy
24
+ * (Charles, Proxyman, mitmweb, etc.) and points the SDK at it.
25
+ */
26
+ interface NetworkCaptureConfig {
27
+ /** When true, attempt to spawn mitmproxy and inject proxy settings into the device. Default: false. */
28
+ readonly enabled?: boolean;
29
+ /** Proxy listen port. Default: 8080. */
30
+ readonly proxyPort?: number;
31
+ /** Proxy listen host. Default: 127.0.0.1. */
32
+ readonly proxyHost?: string;
33
+ /** Path to a pre-generated HAR file to import instead of spawning a proxy. */
34
+ readonly harPath?: string;
35
+ /** Override the directory where mitmproxy writes its JSONL output. Default: `<outputDir>/network`. */
36
+ readonly outputDir?: string;
37
+ /** Skip auto-installing the mitmproxy CA on the device. Useful when the user pre-installed it. */
38
+ readonly skipCertInstall?: boolean;
39
+ /** Only capture calls whose URL matches one of these patterns (string substring or RegExp). */
40
+ readonly includeUrls?: (string | RegExp)[];
41
+ /** Exclude calls matching these patterns. Takes precedence over `includeUrls`. */
42
+ readonly excludeUrls?: (string | RegExp)[];
43
+ /** Header names whose values are replaced with "[REDACTED]". Case-insensitive. */
44
+ readonly redactHeaders?: string[];
45
+ /** Body field names whose values are replaced with "[REDACTED]" at any JSON depth. */
46
+ readonly redactBodyFields?: string[];
47
+ /** Maximum body size to capture (bytes). Larger bodies are truncated. Default: 1 MiB. */
48
+ readonly maxBodyBytes?: number;
49
+ }
50
+ interface ResolvedNetworkCaptureConfig {
51
+ readonly enabled: boolean;
52
+ readonly proxyPort: number;
53
+ readonly proxyHost: string;
54
+ readonly harPath: string | null;
55
+ readonly outputDir: string | null;
56
+ readonly skipCertInstall: boolean;
57
+ readonly includeUrls: readonly (string | RegExp)[];
58
+ readonly excludeUrls: readonly (string | RegExp)[];
59
+ readonly redactHeaders: readonly string[];
60
+ readonly redactBodyFields: readonly string[];
61
+ readonly maxBodyBytes: number;
62
+ }
14
63
  interface MaestroReporterConfig {
15
64
  readonly outputPath?: string;
16
65
  readonly htmlReportPath?: string;
@@ -26,6 +75,7 @@ interface MaestroReporterConfig {
26
75
  readonly cloud?: CloudReporterOptions;
27
76
  readonly reportMode?: ReportMode;
28
77
  readonly quiet?: boolean;
78
+ readonly network?: NetworkCaptureConfig;
29
79
  }
30
80
  interface ResolvedMaestroConfig {
31
81
  readonly outputPath: string;
@@ -42,6 +92,7 @@ interface ResolvedMaestroConfig {
42
92
  readonly cloud: CloudConfig | null;
43
93
  readonly reportMode: ReportMode;
44
94
  readonly quiet: boolean;
95
+ readonly network: ResolvedNetworkCaptureConfig;
45
96
  }
46
97
  interface JUnitProperty {
47
98
  readonly name: string;
@@ -87,7 +138,35 @@ interface MaestroCommandStep {
87
138
  readonly timestamp: string;
88
139
  readonly selector?: string;
89
140
  readonly error?: string;
141
+ /**
142
+ * Raw passthrough of Maestro's metadata block (status / timestamp / duration / sequenceNumber).
143
+ * Kept separate from `details` because consumers like report-orchestrator depend on
144
+ * specific Maestro-internal keys (e.g. `metadata.path` for `startRecording`).
145
+ */
90
146
  readonly metadata?: Record<string, unknown>;
147
+ /**
148
+ * Extracted human-relevant command parameters surfaced in the timeline.
149
+ * Examples:
150
+ * inputText → { text: "<typed value>" }
151
+ * swipe → { direction: "UP", start: "0,0", end: "0,500", duration: 300 }
152
+ * setLocation → { latitude: 37.7749, longitude: -122.4194 }
153
+ * pressKey → { key: "BACK" }
154
+ * assertWithAI → { assertion: "<prompt>" }
155
+ * runScript → { path: "scripts/foo.js" } or { script: "<truncated source>" }
156
+ *
157
+ * Values are post-redaction (see api-redactor / details-redactor).
158
+ */
159
+ readonly details?: Record<string, unknown>;
160
+ /**
161
+ * Sort tiebreaker when two commands share a timestamp (Maestro emits sub-ms timing
162
+ * for fast back-to-back commands). Source: Maestro's metadata.sequenceNumber.
163
+ */
164
+ readonly sequenceNumber?: number;
165
+ /**
166
+ * Sub-commands when this step wraps a flow-control block (`retry`, `repeat`, `runFlow`).
167
+ * Empty / undefined for leaf commands.
168
+ */
169
+ readonly children?: MaestroCommandStep[];
91
170
  }
92
171
  interface MaestroFlowMetadata {
93
172
  readonly appId: string | null;
@@ -169,6 +248,7 @@ interface MaestroTestOptions {
169
248
  readonly env?: Record<string, string>;
170
249
  readonly quiet?: boolean;
171
250
  readonly maestroArgs?: string[];
251
+ readonly network?: NetworkCaptureConfig;
172
252
  }
173
253
  interface MaestroReportOptions {
174
254
  readonly junitPath?: string;
@@ -229,8 +309,16 @@ declare function discoverFlowFiles(dir: string): string[];
229
309
  * Also detects device/platform metadata from log content.
230
310
  */
231
311
 
232
- declare function parseLogContent(content: string): MaestroLogEntry[];
233
- declare function parseLogFile(filePath: string): MaestroLogEntry[];
312
+ /**
313
+ * @param defaultDate Either an ISO date string (`YYYY-MM-DD`) or a Date object.
314
+ * Used to anchor log lines that only carry a wall-clock time (`HH:MM:SS`).
315
+ * Callers should pass the log file's mtime as a Date so the local-zone
316
+ * conversion produces a correct UTC ISO timestamp for log entries. When a
317
+ * plain string is passed, the timestamp is constructed without TZ-shift
318
+ * compensation (legacy behaviour — may be off by the local UTC offset).
319
+ */
320
+ declare function parseLogContent(content: string, defaultDate?: string | Date): MaestroLogEntry[];
321
+ declare function parseLogFile(filePath: string, defaultDate?: string | Date): MaestroLogEntry[];
234
322
  declare function detectPlatformFromLogs(entries: MaestroLogEntry[]): MaestroPlatform;
235
323
  declare function discoverLogFiles(dir: string): string[];
236
324
 
@@ -261,8 +349,13 @@ declare function getArtifactStats(artifacts: CollectedArtifacts): Record<string,
261
349
  * Maps Maestro data to the @testrelic/core TimelineEntry shape.
262
350
  */
263
351
 
264
- declare function buildTimelineEntry(flow: MaestroFlowResult): TimelineEntry;
265
- declare function buildTimeline(flows: MaestroFlowResult[]): TimelineEntry[];
352
+ /**
353
+ * Build a TimelineEntry for a single flow. Optionally accepts an
354
+ * `apiCallsByFlow` map keyed by `testId` (`${flowFile}::${flowName}`) so the
355
+ * orchestrator can plumb network capture results in without re-shaping flows.
356
+ */
357
+ declare function buildTimelineEntry(flow: MaestroFlowResult, apiCallsByFlow?: ReadonlyMap<string, readonly ApiCallRecord[]>): TimelineEntry;
358
+ declare function buildTimeline(flows: MaestroFlowResult[], apiCallsByFlow?: ReadonlyMap<string, readonly ApiCallRecord[]>): TimelineEntry[];
266
359
 
267
360
  /**
268
361
  * Computes Summary stats from TimelineEntry arrays.
@@ -311,9 +404,15 @@ interface MaestroRunResult {
311
404
  readonly debugOutputDir: string;
312
405
  readonly stdout: string;
313
406
  readonly stderr: string;
407
+ /** Directory where the network proxy wrote JSONL records (null when capture was disabled or failed to start). */
408
+ readonly networkJsonlDir: string | null;
314
409
  }
315
410
  declare function buildMaestroArgs(options: MaestroTestOptions, tempDir: string): string[];
316
- declare function runMaestro(options: MaestroTestOptions): Promise<MaestroRunResult>;
411
+ interface RunMaestroExtras {
412
+ /** Optional network-capture config. When `enabled`, mitmproxy is spawned and the device is configured to route through it. */
413
+ readonly network?: ResolvedNetworkCaptureConfig;
414
+ }
415
+ declare function runMaestro(options: MaestroTestOptions, extras?: RunMaestroExtras): Promise<MaestroRunResult>;
317
416
 
318
417
  /**
319
418
  * Report orchestrator — ties all parsers together to produce
@@ -335,6 +434,12 @@ interface OrchestratorInput {
335
434
  * rely on heuristic detection that may return 'unknown'.
336
435
  */
337
436
  readonly platform?: MaestroPlatform;
437
+ /**
438
+ * Directory containing JSONL files emitted by `network-proxy.ts` (mitmproxy
439
+ * addon). When omitted, the orchestrator still picks up `config.network.harPath`
440
+ * if set. Unrelated to Maestro's own debug output.
441
+ */
442
+ readonly networkJsonlDir?: string | null;
338
443
  }
339
444
  interface OrchestratorResult {
340
445
  readonly report: TestRunReport;
@@ -419,6 +524,13 @@ interface TestResultForUpload {
419
524
  readonly timestamp: string;
420
525
  readonly source?: string | null;
421
526
  }>;
527
+ /**
528
+ * Per-flow captured API calls. The cloud platform's `normalizeApiCall` (in
529
+ * `server/src/utils/artifact-normalizer.ts`) accepts this shape directly and
530
+ * stores it as `network.json`. Surfaced in the Test Detail Network panel and
531
+ * via Ask AI's `query_network_logs` tool — works for free, no cloud changes.
532
+ */
533
+ readonly apiCalls?: readonly ApiCallRecord[];
422
534
  }
423
535
  interface RunUploadPayload {
424
536
  readonly runId: string;