* Wait for a flow to complete with a caller-facing timeout race. * * Mirrors the `waitForDesktopFlow` pattern shared across all four services: * - Creates a local timeout promise for this wait call only. * - Races that local timeout against `flow.resultDeferred.promise`. * - Registrat
(flowId: string, timeoutMs: number)
| 111 | * - On any error result (caller timeout or flow error), calls `finish` for shared cleanup. |
| 112 | */ |
| 113 | async waitFor(flowId: string, timeoutMs: number): Promise<Result<void, string>> { |
| 114 | const flow = this.flows.get(flowId); |
| 115 | if (!flow) { |
| 116 | const completed = this.completed.get(flowId); |
| 117 | if (completed) { |
| 118 | return completed.result; |
| 119 | } |
| 120 | |
| 121 | return Err("OAuth flow not found"); |
| 122 | } |
| 123 | |
| 124 | let timeoutHandle: ReturnType<typeof setTimeout> | null = null; |
| 125 | const timeoutPromise = new Promise<Result<void, string>>((resolve) => { |
| 126 | timeoutHandle = setTimeout(() => { |
| 127 | resolve(Err("Timed out waiting for OAuth callback")); |
| 128 | }, timeoutMs); |
| 129 | }); |
| 130 | |
| 131 | const result = await Promise.race([flow.resultDeferred.promise, timeoutPromise]); |
| 132 | |
| 133 | if (timeoutHandle !== null) { |
| 134 | clearTimeout(timeoutHandle); |
| 135 | } |
| 136 | |
| 137 | if (!result.success) { |
| 138 | // Ensure listener is closed on timeout/errors. |
| 139 | void this.finish(flowId, result); |
| 140 | } |
| 141 | |
| 142 | return result; |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Cancel a flow — resolves the deferred with an error and cleans up. |
no test coverage detected