"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 Abort_exports = {};
__export(Abort_exports, {
  Abort: () => Abort
});
module.exports = __toCommonJS(Abort_exports);
var import_MatterError = require("#MatterError.js");
var import_Time = require("#time/Time.js");
var import_Error = require("./Error.js");
var import_Function = require("./Function.js");
var import_Promises = require("./Promises.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
class Abort extends import_Function.Callable {
  // The native controller implementation
  #controller;
  // Optional abort chaining
  #unregisterDependencies;
  // Optional PromiseLike behavior
  #aborted;
  #resolve;
  // Optional timeout
  #timeout;
  constructor({ abort: aborts, timeout, handler, timeoutHandler } = {}) {
    const abort = (reason) => {
      if (typeof reason === "string") {
        reason = new import_MatterError.AbortedError(reason);
      }
      this.abort(reason);
    };
    super(abort);
    this.#controller = new AbortController();
    const throwIfAborted = this.#controller.signal.throwIfAborted.bind(this.#controller.signal);
    this.#controller.signal.throwIfAborted = () => {
      try {
        throwIfAborted();
      } catch (reason) {
        throw createAbortError(reason);
      }
    };
    const self = (reason) => {
      this.abort(reason);
    };
    Object.setPrototypeOf(self, Object.getPrototypeOf(this));
    if (aborts && !Array.isArray(aborts)) {
      aborts = [aborts];
    }
    if (aborts?.length) {
      const dependencies = aborts.map((abort2) => abort2 && ("signal" in abort2 ? abort2.signal : abort2));
      for (const dependency of dependencies) {
        if (dependency === void 0) {
          continue;
        }
        if (dependency.aborted) {
          this.abort((0, import_Error.asError)(dependency.reason));
          continue;
        }
        const listener = () => this.abort((0, import_Error.asError)(dependency.reason));
        dependency.addEventListener("abort", listener);
        const unregisterPrev = this.#unregisterDependencies;
        this.#unregisterDependencies = () => {
          unregisterPrev?.();
          dependency.removeEventListener("abort", listener);
        };
      }
    }
    if (timeout !== void 0) {
      if (timeoutHandler) {
        const original = timeoutHandler;
        timeoutHandler = () => {
          try {
            original.call(this);
          } catch (e) {
            this.abort((0, import_Error.asError)(e));
          }
        };
      } else {
        timeoutHandler = () => this.abort(new import_MatterError.TimeoutError());
      }
      if (timeout <= 0) {
        Promise.resolve().then(() => {
          if (!this.aborted) {
            timeoutHandler.call(this);
          }
        }).catch((_e) => {
        });
      } else {
        this.#timeout = import_Time.Time.getTimer("subtask timeout", timeout, () => {
          if (this.aborted) {
            return;
          }
          timeoutHandler.call(this);
        });
        this.#timeout.start();
      }
    }
    if (handler) {
      if (this.aborted) {
        handler.call(this, this.reason);
      } else {
        this.addEventListener("abort", () => handler.call(this, this.reason));
      }
    }
  }
  abort(reason) {
    if (typeof reason === "string") {
      reason = new import_MatterError.AbortedError(reason);
    }
    this.#controller.abort(reason ?? new import_MatterError.AbortedError("Operation aborted with no reason given"));
    this.close();
  }
  get signal() {
    return this.#controller.signal;
  }
  /**
   * Race one or more promises with my abort signal.
   *
   * If aborted returns undefined.
   */
  async race(...promises) {
    return Abort.race(this, ...promises);
  }
  /**
   * Race with throw on abort.
   */
  async attempt(...promises) {
    return await Abort.attempt(this, ...promises);
  }
  /**
   * Sleep for a duration, returning early if aborted.
   */
  sleep(description, duration) {
    return Abort.sleep(description, this, duration);
  }
  /**
   * Free resources.
   *
   * You must abort or invoke {@link close} when finished if you construct with {@link Abort.Options#abort} or
   * {@link Abort.Options#timeout}.
   */
  close() {
    this.#timeout?.stop();
    this.#unregisterDependencies?.();
  }
  [Symbol.dispose]() {
    this.close();
  }
  if(condition, reason) {
    if (condition) {
      this.abort(reason);
    }
  }
  get aborted() {
    return this.signal.aborted;
  }
  set onabort(onabort) {
    this.signal.onabort = onabort;
  }
  get onabort() {
    return this.signal.onabort;
  }
  get reason() {
    return this.signal.reason;
  }
  throwIfAborted() {
    this.signal.throwIfAborted();
  }
  async then(onfulfilled, onrejected) {
    if (!this.#aborted) {
      if (this.#controller.signal.aborted) {
        return Promise.resolve((0, import_Error.asError)(this.#controller.signal.reason)).then(onfulfilled, onrejected);
      }
      this.#aborted = new Promise((resolve) => this.#resolve = resolve);
      this.addEventListener("abort", () => {
        this.#resolve((0, import_Error.asError)(this.signal.reason));
      });
    }
    return await this.#aborted.then(onfulfilled, onrejected);
  }
  addEventListener(type, listener, options) {
    this.signal.addEventListener(type, listener, options);
  }
  removeEventListener(type, listener, options) {
    this.signal.removeEventListener(type, listener, options);
  }
  dispatchEvent(event) {
    return this.signal.dispatchEvent(event);
  }
}
((Abort2) => {
  function is(signal) {
    if (!signal) {
      return;
    }
    if ("signal" in signal) {
      signal = signal.signal;
    }
    return signal.aborted;
  }
  Abort2.is = is;
  async function race(signal, ...promises) {
    if (signal) {
      if ("signal" in signal) {
        signal = signal.signal;
      }
      let off;
      const aborted = signal.aborted ? Promise.resolve() : new Promise((resolve) => {
        const onabort = () => resolve();
        signal.addEventListener("abort", onabort);
        off = () => signal.removeEventListener("abort", onabort);
      });
      try {
        return await import_Promises.SafePromise.race([aborted, ...promises]);
      } finally {
        off?.();
      }
    }
    if (promises.length === 1) {
      return await Promise.resolve(promises[0]);
    }
    return await import_Promises.SafePromise.race(promises);
  }
  Abort2.race = race;
  async function attempt(signal, ...promises) {
    if (signal && "signal" in signal) {
      signal = signal.signal;
    }
    const result = await race(signal, ...promises);
    signal?.throwIfAborted();
    return result;
  }
  Abort2.attempt = attempt;
  function sleep(description, abort, duration) {
    let timer;
    const rested = new Promise((resolve) => {
      timer = import_Time.Time.getTimer(description, duration, () => resolve()).start();
    });
    return race(abort, rested).finally(timer.stop.bind(timer));
  }
  Abort2.sleep = sleep;
  function subtask(signal, timeout) {
    return new Abort2({ abort: signal, timeout });
  }
  Abort2.subtask = subtask;
  function any(...signals) {
    return new Abort2({ abort: [...signals.filter((signal) => signal)] });
  }
  Abort2.any = any;
  function checkerFor(signal) {
    if (!signal) {
      return () => {
      };
    }
    if ("abort" in signal && typeof signal.abort === "object") {
      signal = signal.abort;
    }
    if (!signal) {
      return () => {
      };
    }
    if ("signal" in signal) {
      signal = signal.signal;
    }
    if (!signal || !("throwIfAborted" in signal)) {
      return () => {
      };
    }
    return signal.throwIfAborted.bind(signal);
  }
  Abort2.checkerFor = checkerFor;
})(Abort || (Abort = {}));
function createAbortError(reason) {
  const error = new import_MatterError.AbortedError();
  const stack = error.stack?.split("\n");
  stack?.splice(1, 2);
  error.stack = stack?.join("\n");
  error.cause = (0, import_Error.asError)(reason);
  return error;
}
//# sourceMappingURL=Abort.js.map
