Files
makelore/electron/coding-runtime/pi/provider-refresh.ts

45 lines
1.4 KiB
TypeScript

export interface PiProviderAuthRecoveryOptions<T> {
accountId: string;
operation: (attempt: 0 | 1) => Promise<T>;
isAuthenticationError: (error: unknown) => boolean;
refreshCredential: () => Promise<void>;
reopenWorker: () => Promise<void>;
}
export class PiProviderRefreshCoordinator {
private readonly refreshes = new Map<string, Promise<void>>();
get pendingAccountCount(): number {
return this.refreshes.size;
}
async refreshAccount(accountId: string, refresh: () => Promise<void>): Promise<void> {
const normalizedAccountId = accountId.trim();
if (!normalizedAccountId) throw new Error('Provider account id is required');
let pending = this.refreshes.get(normalizedAccountId);
if (!pending) {
pending = Promise.resolve()
.then(refresh)
.finally(() => {
if (this.refreshes.get(normalizedAccountId) === pending) {
this.refreshes.delete(normalizedAccountId);
}
});
this.refreshes.set(normalizedAccountId, pending);
}
await pending;
}
async withSingleAuthRecovery<T>(options: PiProviderAuthRecoveryOptions<T>): Promise<T> {
try {
return await options.operation(0);
} catch (error) {
if (!options.isAuthenticationError(error)) throw error;
}
await this.refreshAccount(options.accountId, options.refreshCredential);
await options.reopenWorker();
return await options.operation(1);
}
}