/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { CanceledError } from "#MatterError.js";
import { asError, errorOf } from "./Error.js";
import { MaybePromise } from "./Promises.js";
class CancelablePromise {
  #reject;
  #promise;
  #isSettled = false;
  // Cancelable cannot create its own logger because that would create a circular dependency
  static #logger = console;
  /**
   * Create a new cancelable promise.
   *
   * If the promise is rejected due to cancelation, the {@link executor} callbacks have no effect.
   *
   * If you supply {@link onCancel} it overwrites the {@link CancelablePromise#onCancel} method.
   *
   * @param executor the normal executor supplied to a {@link Promise} constructor
   * @param onCancel rejection handler supplied with a reason and a callback for optionally rejecting the promise
   */
  constructor(executor, onCancel) {
    if (onCancel !== void 0) {
      this.onCancel = onCancel;
    }
    this.#promise = new Promise((resolve, reject) => {
      this.#reject = (reason) => {
        this.#isSettled = true;
        reject(errorOf(reason));
      };
      executor(
        (value) => {
          if (this.#isSettled) {
            return;
          }
          this.#isSettled = true;
          resolve(value);
        },
        (reason) => {
          if (this.#isSettled) {
            CancelablePromise.logger.warn(`Cancelable promise rejected after settle:`, reason);
            return;
          }
          this.#reject(reason);
        }
      );
    });
  }
  /**
   * Cancel the operation.
   */
  cancel(reason = new CanceledError()) {
    if (this.#isSettled) {
      return;
    }
    try {
      this.onCancel(asError(reason));
    } catch (e) {
      this.#reject(e);
    }
  }
  /**
   * Implement cancellation.  This is only invoked if the promise has not resolved.
   *
   * Throwing causes the promise to reject with the error thrown.  The default implementation rethrows {@link reason}.
   *
   * This is overwritten if there is an "onCancel" argument to the constructor.
   */
  onCancel(reason) {
    throw reason;
  }
  then(onfulfilled, onrejected) {
    const result = this.#promise.then(onfulfilled, onrejected);
    result.cancel = this.cancel.bind(this);
    return result;
  }
  catch(onrejected) {
    return this.then(void 0, onrejected);
  }
  finally(onfinally) {
    const handler = (result) => {
      onfinally?.();
      return result;
    };
    return this.then(handler, handler);
  }
  get [Symbol.toStringTag]() {
    return this.#promise[Symbol.toStringTag];
  }
  static is(value) {
    return MaybePromise.is(value) && typeof value.cancel === "function";
  }
  static resolve(value) {
    const result = Promise.resolve(value);
    result.cancel = () => {
    };
    return result;
  }
  static reject(cause) {
    const result = Promise.reject(errorOf(cause));
    result.cancel = () => {
    };
    return result;
  }
  static set logger(logger) {
    this.#logger = logger;
  }
  static get logger() {
    return this.#logger;
  }
}
export {
  CancelablePromise
};
//# sourceMappingURL=Cancelable.js.map
