| 26 | export type { Progress } from '@protocol/progress'; |
| 27 | |
| 28 | export class ProgressController { |
| 29 | private _forceAbortPromise = new ManualPromise<any>(); |
| 30 | private _donePromise = new ManualPromise<void>(); |
| 31 | private _state: 'before' | 'running' | { error: Error } | 'finished' = 'before'; |
| 32 | private _onCallLog?: (message: string) => void; |
| 33 | |
| 34 | readonly metadata: CallMetadata; |
| 35 | private _controller: AbortController; |
| 36 | |
| 37 | constructor(metadata?: CallMetadata, onCallLog?: (message: string) => void) { |
| 38 | this.metadata = metadata || { id: '', startTime: 0, endTime: 0, type: 'Internal', method: '', params: {}, log: [], internal: true }; |
| 39 | this._onCallLog = onCallLog; |
| 40 | this._forceAbortPromise.catch(e => null); // Prevent unhandled promise rejection. |
| 41 | this._controller = new AbortController(); |
| 42 | } |
| 43 | |
| 44 | static createForSdkObject(sdkObject: SdkObject, callMetadata: CallMetadata) { |
| 45 | const logName = sdkObject.logName || 'api'; |
| 46 | return new ProgressController(callMetadata, message => { |
| 47 | // Note: "attribution.playwright" is undefined in DebugController. Unfortunate! |
| 48 | if (logName === 'api' && sdkObject.attribution.playwright?.options.isInternalPlaywright) |
| 49 | return; |
| 50 | debugLogger.log(logName, message); |
| 51 | sdkObject.instrumentation.onCallLog(sdkObject, callMetadata, logName, message); |
| 52 | }); |
| 53 | } |
| 54 | |
| 55 | |
| 56 | async abort(error: Error) { |
| 57 | if (this._state === 'running') { |
| 58 | (error as any)[kAbortErrorSymbol] = true; |
| 59 | this._state = { error }; |
| 60 | this._forceAbortPromise.reject(error); |
| 61 | this._controller.abort(error); |
| 62 | } |
| 63 | await this._donePromise; |
| 64 | } |
| 65 | |
| 66 | async run<T>(task: (progress: Progress) => Promise<T>, timeout?: number): Promise<T> { |
| 67 | const deadline = timeout ? monotonicTime() + timeout : 0; |
| 68 | assert(this._state === 'before'); |
| 69 | this._state = 'running'; |
| 70 | let timer: NodeJS.Timeout | undefined; |
| 71 | |
| 72 | let outerProgress: string | undefined; |
| 73 | let allowConcurrent = false; |
| 74 | |
| 75 | const progress: Progress = { |
| 76 | timeout: timeout ?? 0, |
| 77 | deadline, |
| 78 | disableTimeout: () => { |
| 79 | clearTimeout(timer); |
| 80 | }, |
| 81 | log: message => { |
| 82 | if (this._state === 'running') |
| 83 | this.metadata.log.push(message); |
| 84 | // Note: we might be sending logs after progress has finished, for example browser logs. |
| 85 | this._onCallLog?.(message); |
nothing calls this directly
no outgoing calls
no test coverage detected
searching dependent graphs…