'use strict';

var stderrLib = require('stderr-lib');

// src/utils/batch.ts

// src/types.ts
function err(error) {
  return { ok: false, error };
}

// src/utils/batch.ts
var MIN_CONCURRENCY = 1;
var MAX_CONCURRENCY = 64;
var DEFAULT_CONCURRENCY = 4;
function clampConcurrency(value) {
  if (value === void 0) return DEFAULT_CONCURRENCY;
  return Math.max(MIN_CONCURRENCY, Math.min(MAX_CONCURRENCY, Math.floor(value)));
}
function createBatchState(total) {
  const results = [];
  for (let i = 0; i < total; i += 1) {
    results.push(void 0);
  }
  return { results, succeeded: 0, failed: 0, completed: 0 };
}
async function processItem(item, index, operation, state) {
  let result;
  try {
    result = await operation(item);
  } catch (thrown) {
    result = err(stderrLib.stderr(thrown));
  }
  state.results[index] = result;
  state.completed += 1;
  if (result.ok) {
    state.succeeded += 1;
  } else {
    state.failed += 1;
  }
}
async function batch(items, operation, options) {
  const concurrency = clampConcurrency(options?.concurrency);
  const signal = options?.signal;
  const onProgress = options?.onProgress;
  const total = items.length;
  if (total === 0) {
    return { results: [], succeeded: 0, failed: 0 };
  }
  const state = createBatchState(total);
  const inFlight = /* @__PURE__ */ new Set();
  let nextIndex = 0;
  while (nextIndex < total) {
    if (signal?.aborted) break;
    const itemIndex = nextIndex;
    nextIndex += 1;
    const promise = processItem(items[itemIndex], itemIndex, operation, state).then(() => {
      if (onProgress) {
        const result = state.results[itemIndex];
        onProgress(state.completed, total, result);
      }
    });
    inFlight.add(promise);
    promise.then(
      () => inFlight.delete(promise),
      () => inFlight.delete(promise)
    );
    if (inFlight.size >= concurrency) {
      await Promise.race(inFlight);
    }
  }
  if (inFlight.size > 0) {
    await Promise.all(inFlight);
  }
  return { results: state.results, succeeded: state.succeeded, failed: state.failed };
}

// src/utils/retry.ts
var DEFAULT_MAX_ATTEMPTS = 3;
var DEFAULT_INITIAL_DELAY_MS = 1e3;
var DEFAULT_MAX_DELAY_MS = 3e4;
var DEFAULT_BACKOFF_MULTIPLIER = 2;
var JITTER_FRACTION = 0.1;
var DEFAULT_CONFIG = {
  maxAttempts: DEFAULT_MAX_ATTEMPTS,
  initialDelayMs: DEFAULT_INITIAL_DELAY_MS,
  maxDelayMs: DEFAULT_MAX_DELAY_MS,
  backoffMultiplier: DEFAULT_BACKOFF_MULTIPLIER,
  shouldRetry: void 0,
  signal: void 0,
  onRetry: void 0
};
function resolveConfig(opts) {
  if (!opts) return DEFAULT_CONFIG;
  return {
    ...DEFAULT_CONFIG,
    ...Object.fromEntries(Object.entries(opts).filter(([, v]) => v !== void 0)),
    maxAttempts: Math.max(1, Math.floor(opts.maxAttempts ?? DEFAULT_MAX_ATTEMPTS)),
    initialDelayMs: Math.max(0, opts.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS),
    maxDelayMs: Math.max(0, opts.maxDelayMs ?? DEFAULT_MAX_DELAY_MS),
    backoffMultiplier: Math.max(1, opts.backoffMultiplier ?? DEFAULT_BACKOFF_MULTIPLIER)
  };
}
function computeDelay(attempt, config) {
  const baseDelay = Math.min(config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt), config.maxDelayMs);
  const jitter = baseDelay * JITTER_FRACTION * (2 * Math.random() - 1);
  return Math.max(0, Math.round(baseDelay + jitter));
}
async function waitWithAbort(delayMs, signal) {
  if (signal?.aborted) {
    return false;
  }
  return new Promise((resolve) => {
    const timer = setTimeout(() => {
      cleanup();
      resolve(true);
    }, delayMs);
    const onAbort = () => {
      clearTimeout(timer);
      cleanup();
      resolve(false);
    };
    const cleanup = () => {
      signal?.removeEventListener("abort", onAbort);
    };
    if (signal) {
      signal.addEventListener("abort", onAbort, { once: true });
    }
  });
}
function shouldBreakAfterFailure(attempt, lastError, config) {
  if (attempt === config.maxAttempts - 1) return true;
  if (config.shouldRetry && !config.shouldRetry(lastError, attempt + 1)) return true;
  if (config.signal?.aborted) return true;
  return false;
}
async function retry(operation, options) {
  const config = resolveConfig(options);
  let lastResult = err(new Error("No attempts executed"));
  for (let attempt = 0; attempt < config.maxAttempts; attempt += 1) {
    lastResult = await operation();
    if (lastResult.ok) return lastResult;
    if (shouldBreakAfterFailure(attempt, lastResult.error, config)) break;
    const delayMs = computeDelay(attempt, config);
    if (config.onRetry) config.onRetry(lastResult.error, attempt + 1, delayMs);
    const waitCompleted = await waitWithAbort(delayMs, config.signal);
    if (!waitCompleted) break;
  }
  return lastResult;
}

exports.batch = batch;
exports.retry = retry;
//# sourceMappingURL=utils.cjs.map
//# sourceMappingURL=utils.cjs.map