// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import assert from 'node:assert'; import chalk from 'chalk'; import { $, within } from './core.js'; import { sleep } from './goods.js'; import { parseDuration } from './util.js'; export async function retry(count, a, b) { const total = count; let callback; let delayStatic = 0; let delayGen; if (typeof a == 'function') { callback = a; } else { if (typeof a == 'object') { delayGen = a; } else { delayStatic = parseDuration(a); } assert(b); callback = b; } let lastErr; let attempt = 0; while (count-- > 0) { attempt++; try { return await callback(); } catch (err) { let delay = 0; if (delayStatic > 0) delay = delayStatic; if (delayGen) delay = delayGen.next().value; $.log({ kind: 'retry', error: chalk.bgRed.white(' FAIL ') + ` Attempt: ${attempt}${total == Infinity ? '' : `/${total}`}` + (delay > 0 ? `; next in ${delay}ms` : ''), }); lastErr = err; if (count == 0) break; if (delay) await sleep(delay); } } throw lastErr; } export function* expBackoff(max = '60s', rand = '100ms') { const maxMs = parseDuration(max); const randMs = parseDuration(rand); let n = 1; while (true) { const ms = Math.floor(Math.random() * randMs); yield Math.min(2 ** n++, maxMs) + ms; } } export async function spinner(title, callback) { if (typeof title == 'function') { callback = title; title = ''; } let i = 0; const spin = () => process.stderr.write(` ${'⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'[i++ % 10]} ${title}\r`); return within(async () => { $.verbose = false; const id = setInterval(spin, 100); let result; try { result = await callback(); } finally { clearInterval(id); process.stderr.write(' '.repeat(process.stdout.columns - 1) + '\r'); } return result; }); }