'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

class AbortContext {
  constructor(parent, abortSignal, deadline) {
    this.parent = parent;
    this.abortSignal = abortSignal;
    this.deadline = deadline;
  }
  /**
   * Abort either when the parent aborts, or after the given timeout has
   * expired.
   *
   * @param ctx - The parent context
   * @param timeout - A timeout value, in milliseconds
   * @returns A new context
   */
  static forTimeoutMillis(ctx, timeout) {
    const desiredDeadline = new Date(Date.now() + timeout);
    const actualDeadline = ctx.deadline && ctx.deadline < desiredDeadline ? ctx.deadline : desiredDeadline;
    if (ctx.abortSignal.aborted) {
      if (ctx.deadline && desiredDeadline === actualDeadline) {
        return ctx;
      }
      return new AbortContext(ctx, ctx.abortSignal, actualDeadline);
    }
    const controller = new AbortController();
    const timeoutHandle = setTimeout(abort, timeout);
    ctx.abortSignal.addEventListener("abort", abort);
    function abort() {
      ctx.abortSignal.removeEventListener("abort", abort);
      clearTimeout(timeoutHandle);
      controller.abort();
    }
    return new AbortContext(ctx, controller.signal, actualDeadline);
  }
  /**
   * Abort either when the parent aborts, or when the given controller is
   * triggered.
   *
   * @remarks
   *
   * If you have access to the controller, this function is more efficient than
   * {@link AbortContext#forSignal}.
   *
   * @param ctx - The parent context
   * @param controller - An abort controller
   * @returns A new context
   */
  static forController(ctx, controller) {
    if (ctx.abortSignal.aborted) {
      return ctx;
    } else if (controller.signal.aborted) {
      return new AbortContext(ctx, controller.signal, ctx.deadline);
    }
    function abort() {
      ctx.abortSignal.removeEventListener("abort", abort);
      controller.abort();
    }
    ctx.abortSignal.addEventListener("abort", abort);
    return new AbortContext(ctx, controller.signal, ctx.deadline);
  }
  /**
   * Abort either when the parent aborts, or when the given signal is triggered.
   *
   * @remarks
   *
   * If you have access to the controller and not just the signal,
   * {@link AbortContext#forController} is slightly more efficient to use.
   *
   * @param ctx - The parent context
   * @param signal - An abort signal
   * @returns A new context
   */
  static forSignal(ctx, signal) {
    if (ctx.abortSignal.aborted) {
      return ctx;
    } else if (signal.aborted) {
      return new AbortContext(ctx, signal, ctx.deadline);
    }
    const controller = new AbortController();
    function abort() {
      ctx.abortSignal.removeEventListener("abort", abort);
      signal.removeEventListener("abort", abort);
      controller.abort();
    }
    ctx.abortSignal.addEventListener("abort", abort);
    signal.addEventListener("abort", abort);
    return new AbortContext(ctx, controller.signal, ctx.deadline);
  }
  value(key) {
    return this.parent.value(key);
  }
}

var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class FakeAbortSignal {
  constructor() {
    __publicField(this, "aborted", false);
    __publicField(this, "reason");
  }
  onabort() {
  }
  throwIfAborted() {
  }
  addEventListener() {
  }
  removeEventListener() {
  }
  dispatchEvent() {
    return true;
  }
}
class RootContext {
  constructor() {
    __publicField(this, "abortSignal", new FakeAbortSignal());
    __publicField(this, "deadline");
  }
  value(_key) {
    return void 0;
  }
}

class ValueContext {
  constructor(_parent, _key, _value) {
    this._parent = _parent;
    this._key = _key;
    this._value = _value;
  }
  static forConstantValue(ctx, key, value) {
    return new ValueContext(ctx, key, value);
  }
  get abortSignal() {
    return this._parent.abortSignal;
  }
  get deadline() {
    return this._parent.deadline;
  }
  value(key) {
    return key === this._key ? this._value : this._parent.value(key);
  }
}

class Contexts {
  /**
   * Creates a root context.
   *
   * @remarks
   *
   * This should normally only be called near the root of an application. The
   * created context is meant to be passed down into deeper levels, which may or
   * may not make derived contexts out of it.
   */
  static root() {
    return new RootContext();
  }
  /**
   * Creates a derived context, which signals to abort operations either when
   * any parent context signals, or when the given source is aborted.
   *
   * @remarks
   *
   * If the parent context was already aborted, then it is returned as-is.
   *
   * If the given source was already aborted, then a new already-aborted context
   * is returned.
   *
   * @param parentCtx - A parent context that shall be used as a base
   * @param source - An abort controller or signal that you intend to perhaps
   *                 trigger at some later point in time.
   * @returns A new {@link Context}
   */
  static withAbort(parentCtx, source) {
    return "aborted" in source ? AbortContext.forSignal(parentCtx, source) : AbortContext.forController(parentCtx, source);
  }
  /**
   * Creates a derived context, which signals to abort operations either when
   * any parent context signals, or when the given amount of time has passed.
   * This may affect the deadline.
   *
   * @param parentCtx - A parent context that shall be used as a base
   * @param timeout - The duration of time, after which the derived context will
   *                  signal to abort.
   * @returns A new {@link Context}
   */
  static withTimeoutDuration(parentCtx, timeout) {
    return AbortContext.forTimeoutMillis(parentCtx, timeout.as("milliseconds"));
  }
  /**
   * Creates a derived context, which signals to abort operations either when
   * any parent context signals, or when the given amount of time has passed.
   * This may affect the deadline.
   *
   * @param parentCtx - A parent context that shall be used as a base
   * @param timeout - The number of milliseconds, after which the derived
   *                  context will signal to abort.
   * @returns A new {@link Context}
   */
  static withTimeoutMillis(parentCtx, timeout) {
    return AbortContext.forTimeoutMillis(parentCtx, timeout);
  }
  /**
   * Creates a derived context, which has a specific key-value pair set as well
   * as all key-value pairs that were set in the original context.
   *
   * @param parentCtx - A parent context that shall be used as a base
   * @param key - The key of the value to set
   * @param value - The value, or a function that accepts the previous value (or
   *                undefined if not set yet) and computes the new value
   * @returns A new {@link Context}
   */
  static withValue(parentCtx, key, value) {
    const v = typeof value === "function" ? value(parentCtx.value(key)) : value;
    return ValueContext.forConstantValue(parentCtx, key, v);
  }
}

exports.Contexts = Contexts;
//# sourceMappingURL=alpha.cjs.js.map
