| |
| |
| |
| |
| |
|
|
| import vscode from 'vscode'; |
|
|
| const EXCHANGE_TIMEOUT_MS = 60_000; |
|
|
| |
| |
| |
| |
| interface DisposablePromise<T> extends vscode.Disposable { |
| promise: Promise<T>; |
| } |
|
|
| |
| |
| |
| |
| export class CodeManager implements vscode.Disposable { |
| private readonly inFlightPromises = new Map< |
| string, |
| { resolve: (value: string) => void; reject: (reason: Error) => void } |
| >(); |
|
|
| |
| |
| |
| |
| dispose(): void { |
| const error = new Error('Authentication provider has been disposed.'); |
| for (const promiseHandlers of this.inFlightPromises.values()) { |
| promiseHandlers.reject(error); |
| } |
| this.inFlightPromises.clear(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| async waitForCode( |
| nonce: string, |
| token: vscode.CancellationToken, |
| ): Promise<string> { |
| if (this.inFlightPromises.has(nonce)) { |
| throw new Error(`Already waiting for nonce: ${nonce}`); |
| } |
|
|
| const userCancellation = waitForCancellation(token); |
| const timeout = waitForTimeout(EXCHANGE_TIMEOUT_MS); |
|
|
| try { |
| const codePromise = new Promise<string>((resolve, reject) => { |
| this.inFlightPromises.set(nonce, { resolve, reject }); |
| }); |
|
|
| |
| return await Promise.race([ |
| codePromise, |
| userCancellation.promise, |
| timeout.promise, |
| ]); |
| } finally { |
| this.inFlightPromises.delete(nonce); |
| userCancellation.dispose(); |
| timeout.dispose(); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| resolveCode(nonce: string, code: string): void { |
| const inFlight = this.inFlightPromises.get(nonce); |
| if (!inFlight) { |
| throw new Error('Unexpected code exchange received'); |
| } |
|
|
| inFlight.resolve(code); |
| } |
| } |
|
|
| |
| |
| |
| |
| function waitForCancellation( |
| token: vscode.CancellationToken, |
| ): DisposablePromise<never> { |
| let listener: vscode.Disposable; |
| const promise = new Promise<never>((_, reject) => { |
| listener = token.onCancellationRequested(() => { |
| reject(new Error('Authentication was cancelled by the user')); |
| }); |
| }); |
|
|
| return { |
| promise, |
| |
| dispose: () => listener.dispose(), |
| }; |
| } |
|
|
| |
| |
| |
| |
| function waitForTimeout(ms: number): DisposablePromise<never> { |
| let timeoutId: NodeJS.Timeout; |
| const promise = new Promise<never>((_, reject) => { |
| timeoutId = setTimeout(() => { |
| reject(new Error('Exchange timeout exceeded')); |
| }, ms); |
| }); |
|
|
| return { |
| promise, |
| dispose: () => { |
| clearTimeout(timeoutId); |
| }, |
| }; |
| } |
|
|