@@ -120,9 +120,10 @@ export class AccessibilityAuditService {
120120 * @param options Timeout for the completion callback.
121121 */
122122 async runAudit ( auditTypes : string [ ] , options : RunAuditOptions = { } ) : Promise < AxAuditIssue [ ] > {
123- // Issues are streamed one per `hostFoundAuditIssue:` call, not returned in
124- // the completion callback (whose argument list is empty). Collect them as
125- // they arrive and stop when the completion call lands.
123+ // Issues are streamed one per `hostFoundAuditIssue:` call and the
124+ // completion callback carries no arguments. Older releases are reported to
125+ // return them in the completion instead, so both are collected and the
126+ // streamed set wins when present.
126127 const issues : AxAuditIssue [ ] = [ ] ;
127128 const stopIssues = this . transport . onInbound ( 'hostFoundAuditIssue:' , ( args ) => {
128129 const issue = deserializeAxObject ( args [ 0 ] ) ;
@@ -140,7 +141,8 @@ export class AccessibilityAuditService {
140141
141142 try {
142143 if ( options . targetPid !== undefined ) {
143- // Without a target the daemon audits nothing and reports no issues.
144+ // Narrows the audit to one process; omitted, the daemon uses the
145+ // foreground app.
144146 const pidAux = new MessageAux ( ) ;
145147 pidAux . appendObj ( options . targetPid ) ;
146148 this . transport . invokeOneway ( 'deviceSetAuditTargetPid:' , pidAux ) ;
@@ -152,11 +154,11 @@ export class AccessibilityAuditService {
152154 const aux = new MessageAux ( ) ;
153155 aux . appendObj ( auditTypes ) ;
154156 this . transport . invokeOneway ( 'deviceBeginAuditTypes:' , aux ) ;
155- await completion ;
157+ const completionArgs = await completion ;
156158 // The completion call can land marginally before the last issue is read
157159 // off the socket, so let the queue drain.
158160 await new Promise ( ( resolve ) => setTimeout ( resolve , 250 ) ) ;
159- return issues ;
161+ return issues . length > 0 ? issues : issuesFromCompletion ( completionArgs ) ;
160162 } finally {
161163 stopIssues ( ) ;
162164 stopLog ?.( ) ;
@@ -306,10 +308,10 @@ export type AxAuditIssue = Record<string, unknown>;
306308/** Options for {@link AccessibilityAuditService.runAudit}. */
307309export interface RunAuditOptions {
308310 /**
309- * PID of the app to audit.
311+ * PID of the app to audit. Optional — the daemon audits the foreground app
312+ * when this is omitted.
310313 *
311- * Strongly recommended: with no target the daemon audits nothing and reports
312- * zero issues, which is indistinguishable from a clean pass.
314+ * Set it to audit a specific process regardless of what is frontmost.
313315 */
314316 targetPid ?: number ;
315317 /** How long to wait for the audit to complete, in milliseconds. */
@@ -318,6 +320,24 @@ export interface RunAuditOptions {
318320 onLog ?: ( line : string ) => void ;
319321}
320322
323+ /**
324+ * Recovers audit issues carried in the completion callback's arguments.
325+ *
326+ * The device sends no arguments there and streams each issue separately, so this is
327+ * the fallback path for releases that report them in the completion instead.
328+ */
329+ function issuesFromCompletion ( args : unknown [ ] ) : AxAuditIssue [ ] {
330+ if ( args . length === 0 ) {
331+ return [ ] ;
332+ }
333+ const payload = deserializeAxObject ( args [ 0 ] ) ;
334+ if ( payload === null || payload === undefined ) {
335+ return [ ] ;
336+ }
337+ const list = Array . isArray ( payload ) ? payload : [ payload ] ;
338+ return list . filter ( ( issue ) : issue is AxAuditIssue => typeof issue === 'object' && issue !== null ) ;
339+ }
340+
321341/** Narrows an unknown reply to `string[]`. */
322342function asStringArray ( value : unknown , selector : string ) : string [ ] {
323343 if ( ! Array . isArray ( value ) || ! value . every ( ( entry ) => typeof entry === 'string' ) ) {
0 commit comments