"use strict";
/* Socket Lib - Built with esbuild */
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var lock_manager_exports = {};
__export(lock_manager_exports, {
  ProcessLockManager: () => ProcessLockManager
});
module.exports = __toCommonJS(lock_manager_exports);
var import_message = require("../errors/message");
var import_safe = require("../fs/safe");
var import_logger = require("../logger/logger");
var import_date = require("../primordials/date");
var import_error = require("../primordials/error");
var import_map_set = require("../primordials/map-set");
var import_math = require("../primordials/math");
var import_retry = require("../promises/retry");
var import_register = require("../signal-exit/register");
var import_internal = require("./_internal");
const logger = (0, import_logger.getDefaultLogger)();
class ProcessLockManager {
  activeLocks = new import_map_set.SetCtor();
  touchTimers = new import_map_set.MapCtor();
  exitHandlerRegistered = false;
  /**
   * Ensure process exit handler is registered for cleanup. Registers a handler
   * that cleans up all active locks when the process exits.
   */
  ensureExitHandler() {
    if (this.exitHandlerRegistered) {
      return;
    }
    (0, import_register.onExit)(() => {
      for (const timer of this.touchTimers.values()) {
        clearInterval(timer);
      }
      this.touchTimers.clear();
      const fs = (0, import_internal.getFs)();
      for (const lockPath of this.activeLocks) {
        try {
          if (fs.existsSync(lockPath)) {
            (0, import_safe.safeDeleteSync)(lockPath, { recursive: true });
          }
        } catch {
        }
      }
    });
    this.exitHandlerRegistered = true;
  }
  /**
   * Touch a lock file to update its mtime. This prevents the lock from being
   * detected as stale during long operations.
   *
   * @param lockPath - Path to the lock directory.
   */
  touchLock(lockPath) {
    try {
      const fs = (0, import_internal.getFs)();
      if (fs.existsSync(lockPath)) {
        const now = (0, import_date.DateNow)() / 1e3;
        fs.utimesSync(lockPath, now, now);
      }
    } catch (e) {
      logger.warn(`Failed to touch lock ${lockPath}: ${(0, import_message.errorMessage)(e)}`);
    }
  }
  /**
   * Start periodic touching of a lock file. Aligned with npm npx strategy to
   * prevent false stale detection.
   *
   * @param lockPath - Path to the lock directory.
   * @param intervalMs - Touch interval in milliseconds.
   */
  startTouchTimer(lockPath, intervalMs) {
    if (intervalMs <= 0 || this.touchTimers.has(lockPath)) {
      return;
    }
    const timer = setInterval(() => {
      this.touchLock(lockPath);
    }, intervalMs);
    timer.unref();
    this.touchTimers.set(lockPath, timer);
  }
  /**
   * Stop periodic touching of a lock file.
   *
   * @param lockPath - Path to the lock directory.
   */
  stopTouchTimer(lockPath) {
    const timer = this.touchTimers.get(lockPath);
    if (timer) {
      clearInterval(timer);
      this.touchTimers.delete(lockPath);
    }
  }
  /**
   * Check if a lock is stale based on mtime. Uses second-level granularity to
   * avoid APFS floating-point precision issues. Aligned with npm's npx locking
   * strategy.
   *
   * @param lockPath - Path to the lock directory.
   * @param staleMs - Stale timeout in milliseconds.
   *
   * @returns True if lock exists and is stale
   */
  isStale(lockPath, staleMs) {
    try {
      const stats = (0, import_internal.getFs)().statSync(lockPath, { throwIfNoEntry: false });
      if (!stats) {
        return false;
      }
      return (0, import_date.DateNow)() - stats.mtime.getTime() > staleMs;
    } catch {
      return false;
    }
  }
  /**
   * Acquire a lock using mkdir for atomic operation. Handles stale locks and
   * includes exit cleanup.
   *
   * This method attempts to create a lock directory atomically. If the lock
   * already exists, it checks if it's stale and removes it before retrying.
   * Uses exponential backoff with jitter for retry attempts.
   *
   * @example
   *   ;```typescript
   *   const release = await processLock.acquire('/tmp/my-lock')
   *   try {
   *     // Critical section
   *   } finally {
   *     release()
   *   }
   *   ```
   *
   * @param lockPath - Path to the lock directory.
   * @param options - Lock acquisition options.
   *
   * @returns Release function to unlock
   *
   * @throws Error if lock cannot be acquired after all retries
   */
  async acquire(lockPath, options = {}) {
    const {
      baseDelayMs = 100,
      maxDelayMs = 1e3,
      retries = 3,
      staleMs = 5e3,
      touchIntervalMs = 2e3
    } = options;
    this.ensureExitHandler();
    return await (0, import_retry.pRetry)(
      async () => {
        try {
          if (this.isStale(lockPath, staleMs)) {
            logger.log(`Removing stale lock: ${lockPath}`);
            try {
              (0, import_safe.safeDeleteSync)(lockPath, { recursive: true });
            } catch {
            }
          }
          const fs = (0, import_internal.getFs)();
          const parent = (0, import_internal.getPath)().dirname(lockPath);
          if (parent && parent !== "." && parent !== lockPath) {
            fs.mkdirSync(parent, { recursive: true });
          }
          fs.mkdirSync(lockPath);
          this.activeLocks.add(lockPath);
          this.startTouchTimer(lockPath, touchIntervalMs);
          return () => this.release(lockPath);
        } catch (e) {
          const code = e.code;
          if (code === "EEXIST") {
            if (this.isStale(lockPath, staleMs)) {
              throw new import_error.ErrorCtor(`Stale lock detected: ${lockPath}`);
            }
            throw new import_error.ErrorCtor(`Lock already exists: ${lockPath}`);
          }
          if (code === "EACCES" || code === "EPERM") {
            throw new import_error.ErrorCtor(
              `Permission denied creating lock: ${lockPath}. Check directory permissions or run with appropriate access.`,
              { cause: e }
            );
          }
          if (code === "EROFS") {
            throw new import_error.ErrorCtor(
              `Cannot create lock on read-only filesystem: ${lockPath}`,
              { cause: e }
            );
          }
          if (code === "ENOTDIR") {
            const lastSlashIndex = (0, import_math.MathMax)(
              lockPath.lastIndexOf("/"),
              lockPath.lastIndexOf("\\")
            );
            const parentDir = lastSlashIndex === -1 ? "." : lockPath.slice(0, lastSlashIndex);
            throw new import_error.ErrorCtor(
              `Cannot create lock directory: ${lockPath}
A path component is a file when it should be a directory.
Parent path: ${parentDir}
To resolve:
  1. Check if "${parentDir}" contains a file instead of a directory
  2. Remove any conflicting files in the path
  3. Ensure the full parent directory structure exists`,
              { cause: e }
            );
          }
          if (code === "ENOENT") {
            const lastSlashIndex = (0, import_math.MathMax)(
              lockPath.lastIndexOf("/"),
              lockPath.lastIndexOf("\\")
            );
            const parentDir = lastSlashIndex === -1 ? "." : lockPath.slice(0, lastSlashIndex);
            throw new import_error.ErrorCtor(
              `Cannot create lock directory: ${lockPath}
Parent directory does not exist: ${parentDir}
To resolve:
  1. Ensure the parent directory "${parentDir}" exists
  2. Create the directory structure: mkdir -p "${parentDir}"
  3. Check filesystem permissions allow directory creation`,
              { cause: e }
            );
          }
          throw new import_error.ErrorCtor(`Failed to acquire lock: ${lockPath}`, {
            cause: e
          });
        }
      },
      {
        retries,
        baseDelayMs,
        maxDelayMs,
        jitter: true
      }
    );
  }
  /**
   * Release a lock and remove from tracking. Stops periodic touching and
   * removes the lock directory.
   *
   * @example
   *   ;```typescript
   *   processLock.release('/tmp/my-lock')
   *   ```
   *
   * @param lockPath - Path to the lock directory.
   */
  release(lockPath) {
    this.stopTouchTimer(lockPath);
    try {
      if ((0, import_internal.getFs)().existsSync(lockPath)) {
        (0, import_safe.safeDeleteSync)(lockPath, { recursive: true });
      }
      this.activeLocks.delete(lockPath);
    } catch (e) {
      logger.warn(`Failed to release lock ${lockPath}: ${(0, import_message.errorMessage)(e)}`);
    }
  }
  /**
   * Execute a function with exclusive lock protection. Automatically handles
   * lock acquisition, execution, and cleanup.
   *
   * This is the recommended way to use process locks, as it guarantees cleanup
   * even if the callback throws an error.
   *
   * @example
   *   ;```typescript
   *   const result = await processLock.withLock('/tmp/my-lock', async () => {
   *     // Critical section
   *     return someValue
   *   })
   *   ```
   *
   * @param lockPath - Path to the lock directory.
   * @param fn - Function to execute while holding the lock.
   * @param options - Lock acquisition options.
   *
   * @returns Result of the callback function
   *
   * @throws Error from callback or lock acquisition failure
   */
  async withLock(lockPath, fn, options) {
    const release = await this.acquire(lockPath, options);
    try {
      return await fn();
    } finally {
      release();
    }
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  ProcessLockManager
});
