'use strict';

var crypto = require('crypto');

// src/utils/duration.ts
var MAX_DURATION_MS = 4294967295;
var DURATION_REGEX = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/i;
var UNIT_TO_MS = {
  ms: 1,
  s: 1e3,
  m: 6e4,
  h: 36e5,
  d: 864e5
};
function parseDuration(value) {
  if (typeof value === "number") {
    if (!Number.isFinite(value) || value < 0) {
      throw new Error(`Invalid duration: ${value}. Must be a non-negative finite number.`);
    }
    return Math.min(Math.floor(value), MAX_DURATION_MS);
  }
  if (typeof value !== "string" || value.trim() === "") {
    throw new Error(`Invalid duration: "${value}". Expected a duration string (e.g. "5m", "2h") or number.`);
  }
  const match = value.trim().match(DURATION_REGEX);
  if (!match) {
    throw new Error(
      `Invalid duration: "${value}". Expected format: <number><unit> where unit is ms, s, m, h, or d.`
    );
  }
  const amount = parseFloat(match[1]);
  const unit = match[2].toLowerCase();
  const ms = Math.floor(amount * UNIT_TO_MS[unit]);
  if (ms < 0 || ms > MAX_DURATION_MS) {
    throw new Error(`Duration "${value}" exceeds maximum allowed (${MAX_DURATION_MS}ms / ~49.7 days).`);
  }
  return ms;
}
function formatDuration(ms) {
  if (!Number.isFinite(ms) || ms < 0) return "0ms";
  if (ms < 1e3) return `${ms}ms`;
  const days = Math.floor(ms / 864e5);
  const hours = Math.floor(ms % 864e5 / 36e5);
  const minutes = Math.floor(ms % 36e5 / 6e4);
  const seconds = Math.floor(ms % 6e4 / 1e3);
  const parts = [];
  if (days > 0) parts.push(`${days}d`);
  if (hours > 0) parts.push(`${hours}h`);
  if (minutes > 0) parts.push(`${minutes}m`);
  if (seconds > 0) parts.push(`${seconds}s`);
  return parts.join(" ") || "0ms";
}

// src/utils/ip.ts
var PLATFORM_HEADERS = {
  cloudflare: "cf-connecting-ip",
  vercel: "x-real-ip",
  flyio: "fly-client-ip",
  render: "x-render-client-ip",
  firebase: "x-appengine-user-ip",
  "aws-alb": "x-forwarded-for"
};
function detectPlatform() {
  const env = typeof process !== "undefined" ? process.env : {};
  if (env.CF_PAGES || env.CF_WORKERS) return "cloudflare";
  if (env.VERCEL) return "vercel";
  if (env.FLY_APP_NAME) return "flyio";
  if (env.RENDER) return "render";
  if (env.FIREBASE_CONFIG || env.GCLOUD_PROJECT) return "firebase";
  if (env.AWS_EXECUTION_ENV || env.AWS_LAMBDA_FUNCTION_NAME) return "aws-alb";
  return "generic";
}
var _cachedPlatform = null;
function getCachedPlatform() {
  if (_cachedPlatform === null) {
    _cachedPlatform = detectPlatform();
  }
  return _cachedPlatform;
}
var MAX_IP_LENGTH = 45;
function sanitizeIp(ip) {
  const trimmed = ip.trim();
  if (trimmed.length > MAX_IP_LENGTH) return trimmed.slice(0, MAX_IP_LENGTH);
  return trimmed;
}
function getHeader(req, name) {
  const val = req.headers[name];
  if (Array.isArray(val)) return val[0];
  return val;
}
function parseForwardedFor(header, trustedProxyCount) {
  const ips = header.split(",").map((ip) => ip.trim()).filter(Boolean);
  if (ips.length === 0) return void 0;
  const clientIndex = Math.max(0, ips.length - trustedProxyCount);
  return ips[clientIndex] || void 0;
}
function detectClientIp(req, options = {}) {
  const { platform = "auto", trustedProxyCount = 1 } = options;
  const r = req;
  const resolvedPlatform = platform === "auto" ? getCachedPlatform() : platform;
  if (resolvedPlatform !== "generic" && resolvedPlatform in PLATFORM_HEADERS) {
    const headerName = PLATFORM_HEADERS[resolvedPlatform];
    if (headerName) {
      if (resolvedPlatform === "aws-alb") {
        const xff2 = getHeader(r, "x-forwarded-for");
        if (xff2) {
          const ip = parseForwardedFor(xff2, trustedProxyCount);
          if (ip) return sanitizeIp(ip);
        }
      } else {
        const ip = getHeader(r, headerName);
        if (ip) return sanitizeIp(ip);
      }
    }
  }
  if (r.ip) return sanitizeIp(r.ip);
  const xff = getHeader(r, "x-forwarded-for");
  if (xff) {
    const ip = parseForwardedFor(xff, trustedProxyCount);
    if (ip) return sanitizeIp(ip);
  }
  const realIp = getHeader(r, "x-real-ip");
  if (realIp) return sanitizeIp(realIp);
  const socketIp = r.socket?.remoteAddress ?? r.connection?.remoteAddress;
  if (socketIp) return sanitizeIp(socketIp);
  return "unknown";
}
function isPrivateIp(ip) {
  const normalized = ip.startsWith("::ffff:") ? ip.slice(7) : ip;
  if (/^127\./.test(normalized)) return true;
  if (/^10\./.test(normalized)) return true;
  if (/^172\.(1[6-9]|2\d|3[01])\./.test(normalized)) return true;
  if (/^192\.168\./.test(normalized)) return true;
  if (/^169\.254\./.test(normalized)) return true;
  if (/^0\./.test(normalized)) return true;
  if (ip === "::1") return true;
  if (/^fe80:/i.test(ip)) return true;
  if (/^fc00:/i.test(ip)) return true;
  if (/^fd/i.test(ip)) return true;
  return false;
}
function getHeader2(req, name) {
  const val = req.headers[name];
  if (Array.isArray(val)) return val[0] ?? "";
  return val ?? "";
}
function fingerprint(req, options = {}) {
  const {
    ip = true,
    userAgent = true,
    accept = true,
    acceptLanguage = true,
    acceptEncoding = true,
    custom = [],
    ipOptions
  } = options;
  const components = [];
  if (ip) {
    components.push(`ip:${detectClientIp(req, ipOptions)}`);
  }
  if (userAgent) {
    components.push(`ua:${getHeader2(req, "user-agent")}`);
  }
  if (accept) {
    components.push(`accept:${getHeader2(req, "accept")}`);
  }
  if (acceptLanguage) {
    components.push(`lang:${getHeader2(req, "accept-language")}`);
  }
  if (acceptEncoding) {
    components.push(`enc:${getHeader2(req, "accept-encoding")}`);
  }
  for (const c of custom) {
    if (c !== null && c !== void 0) components.push(`custom:${c}`);
  }
  components.sort();
  const hash = crypto.createHash("sha256");
  hash.update(components.join("|"));
  return hash.digest("hex");
}

exports.detectClientIp = detectClientIp;
exports.fingerprint = fingerprint;
exports.formatDuration = formatDuration;
exports.isPrivateIp = isPrivateIp;
exports.parseDuration = parseDuration;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map