"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 utils_exports = {};
__export(utils_exports, {
  AsyncIterableQueue: () => AsyncIterableQueue,
  AudioEnergyFilter: () => AudioEnergyFilter,
  CancellablePromise: () => CancellablePromise,
  Event: () => Event,
  ExpFilter: () => ExpFilter,
  Future: () => Future,
  IdleTimeoutError: () => IdleTimeoutError,
  InvalidErrorType: () => InvalidErrorType,
  Queue: () => Queue,
  Task: () => Task,
  TaskResult: () => TaskResult,
  asError: () => asError,
  cancelAndWait: () => cancelAndWait,
  combineSignals: () => combineSignals,
  createImmutableArray: () => createImmutableArray,
  dedent: () => dedent,
  delay: () => delay,
  gracefullyCancel: () => gracefullyCancel,
  isCloud: () => isCloud,
  isDevMode: () => isDevMode,
  isFfmpegTeardownError: () => isFfmpegTeardownError,
  isHosted: () => isHosted,
  isImmutableArray: () => isImmutableArray,
  isPending: () => isPending,
  isStreamClosedError: () => isStreamClosedError,
  isWritableStreamClosedError: () => isWritableStreamClosedError,
  mergeFrames: () => mergeFrames,
  noop: () => noop,
  readStream: () => readStream,
  rejectOnAbort: () => rejectOnAbort,
  resampleStream: () => resampleStream,
  resolveEnvVar: () => resolveEnvVar,
  shortuuid: () => shortuuid,
  startSoon: () => startSoon,
  toError: () => toError,
  waitFor: () => waitFor,
  waitForAbort: () => waitForAbort,
  waitForParticipant: () => waitForParticipant,
  waitForParticipantAttribute: () => waitForParticipantAttribute,
  waitForTrackPublication: () => waitForTrackPublication,
  waitUntilTimeout: () => waitUntilTimeout,
  withResolvers: () => withResolvers
});
module.exports = __toCommonJS(utils_exports);
var import_rtc_node = require("@livekit/rtc-node");
var import_throws = require("@livekit/throws-transformer/throws");
var import_node_async_hooks = require("node:async_hooks");
var import_node_crypto = require("node:crypto");
var import_node_events = require("node:events");
var import_web = require("node:stream/web");
var import_log = require("./log.cjs");
const noop = () => {
};
const isPending = async (promise) => {
  const sentinel = Symbol("sentinel");
  const result = await Promise.race([
    import_throws.ThrowsPromise.fromPromise(promise),
    import_throws.ThrowsPromise.resolve(sentinel)
  ]);
  return result === sentinel;
};
const mergeFrames = (buffer) => {
  if (Array.isArray(buffer)) {
    buffer = buffer;
    if (buffer.length == 0) {
      throw new TypeError("buffer is empty");
    }
    const sampleRate = buffer[0].sampleRate;
    const channels = buffer[0].channels;
    let samplesPerChannel = 0;
    let data = new Int16Array();
    for (const frame of buffer) {
      if (frame.sampleRate !== sampleRate) {
        throw new TypeError("sample rate mismatch");
      }
      if (frame.channels !== channels) {
        throw new TypeError("channel count mismatch");
      }
      data = new Int16Array([...data, ...frame.data]);
      samplesPerChannel += frame.samplesPerChannel;
    }
    return new import_rtc_node.AudioFrame(data, sampleRate, channels, samplesPerChannel);
  }
  return buffer;
};
class Queue {
  /** @internal */
  items = [];
  #limit;
  #events = new import_node_events.EventEmitter();
  constructor(limit) {
    this.#limit = limit;
  }
  async get(options = {}) {
    while (this.items.length === 0) {
      await (0, import_node_events.once)(this.#events, "put", { signal: options.signal });
    }
    const item = this.items.shift();
    this.#events.emit("get");
    if (typeof item === "undefined") {
      return this.get(options);
    }
    return item;
  }
  async put(item) {
    if (this.#limit && this.items.length >= this.#limit) {
      await (0, import_node_events.once)(this.#events, "get");
    }
    this.items.push(item);
    this.#events.emit("put");
  }
}
class Future {
  #await;
  #resolvePromise;
  #rejectPromise;
  #done = false;
  #rejected = false;
  #result = void 0;
  #error = void 0;
  constructor() {
    this.#await = new import_throws.ThrowsPromise((resolve, reject) => {
      this.#resolvePromise = resolve;
      this.#rejectPromise = reject;
    });
  }
  get await() {
    return this.#await;
  }
  get done() {
    return this.#done;
  }
  get result() {
    if (!this.#done) {
      throw new Error("Future is not done");
    }
    if (this.#rejected) {
      throw this.#error;
    }
    return this.#result;
  }
  /** Whether the future was rejected (cancelled) */
  get rejected() {
    return this.#rejected;
  }
  resolve(value) {
    this.#done = true;
    this.#result = value;
    this.#resolvePromise(value);
  }
  reject(error) {
    this.#done = true;
    this.#rejected = true;
    this.#error = error;
    this.#rejectPromise(error);
    void this.#await.catch(() => void 0);
  }
}
class Event {
  #isSet = false;
  #waiters = [];
  async wait() {
    if (this.#isSet) return true;
    let resolve = noop;
    const waiter = new import_throws.ThrowsPromise((r) => {
      resolve = r;
      this.#waiters.push(resolve);
    });
    try {
      await waiter;
      return true;
    } finally {
      const index = this.#waiters.indexOf(resolve);
      if (index !== -1) {
        this.#waiters.splice(index, 1);
      }
    }
  }
  get isSet() {
    return this.#isSet;
  }
  set() {
    if (this.#isSet) return;
    this.#isSet = true;
    this.#waiters.forEach((resolve) => resolve());
    this.#waiters = [];
  }
  clear() {
    this.#isSet = false;
  }
}
class CancellablePromise {
  #promise;
  #cancelFn;
  #isCancelled = false;
  #error = null;
  constructor(executor) {
    let cancel;
    this.#promise = new import_throws.ThrowsPromise((resolve, reject) => {
      executor(
        resolve,
        (reason) => {
          this.#error = reason;
          reject(reason);
        },
        (cancelFn) => {
          cancel = () => {
            this.#isCancelled = true;
            cancelFn();
          };
        }
      );
    });
    this.#cancelFn = cancel;
  }
  get isCancelled() {
    return this.#isCancelled;
  }
  get error() {
    return this.#error;
  }
  then(onfulfilled, onrejected) {
    return this.#promise.then(onfulfilled, onrejected);
  }
  catch(onrejected) {
    return this.#promise.catch(onrejected);
  }
  finally(onfinally) {
    return this.#promise.finally(onfinally);
  }
  cancel() {
    this.#cancelFn();
  }
  static from(promise) {
    return new CancellablePromise((resolve, reject) => {
      promise.then(resolve).catch(reject);
    });
  }
}
async function gracefullyCancel(promise) {
  if (!promise.isCancelled) {
    promise.cancel();
  }
  try {
    await promise;
  } catch (error) {
  }
}
class AsyncIterableQueue {
  static CLOSE_SENTINEL = Symbol("CLOSE_SENTINEL");
  #queue = new Queue();
  #closed = false;
  get closed() {
    return this.#closed;
  }
  put(item) {
    if (this.#closed) {
      throw new Error("Queue is closed");
    }
    this.#queue.put(item);
  }
  close() {
    this.#closed = true;
    this.#queue.put(AsyncIterableQueue.CLOSE_SENTINEL);
  }
  async next() {
    if (this.#closed && this.#queue.items.length === 0) {
      return { value: void 0, done: true };
    }
    const item = await this.#queue.get();
    if (item === AsyncIterableQueue.CLOSE_SENTINEL && this.#closed) {
      return { value: void 0, done: true };
    }
    return { value: item, done: false };
  }
  [Symbol.asyncIterator]() {
    return this;
  }
}
class ExpFilter {
  #alpha;
  #min;
  #max;
  #filtered = void 0;
  constructor(alphaOrOpts, max) {
    if (typeof alphaOrOpts === "number") {
      this.#alpha = alphaOrOpts;
      this.#max = max;
      return;
    }
    this.#validateAlpha(alphaOrOpts.alpha);
    this.#alpha = alphaOrOpts.alpha;
    this.#filtered = alphaOrOpts.initial;
    this.#min = alphaOrOpts.minVal;
    this.#max = alphaOrOpts.maxVal;
  }
  reset(alphaOrOpts) {
    if (typeof alphaOrOpts === "object") {
      if (alphaOrOpts.alpha !== void 0) {
        this.#validateAlpha(alphaOrOpts.alpha);
        this.#alpha = alphaOrOpts.alpha;
      }
      if (alphaOrOpts.initial !== void 0) {
        this.#filtered = alphaOrOpts.initial;
      }
      if (alphaOrOpts.minVal !== void 0) {
        this.#min = alphaOrOpts.minVal;
      }
      if (alphaOrOpts.maxVal !== void 0) {
        this.#max = alphaOrOpts.maxVal;
      }
      return;
    }
    if (alphaOrOpts) {
      this.#alpha = alphaOrOpts;
    }
    this.#filtered = void 0;
  }
  apply(exp, sample) {
    if (this.#filtered !== void 0) {
      const a = this.#alpha ** exp;
      this.#filtered = a * this.#filtered + (1 - a) * sample;
    } else {
      this.#filtered = sample;
    }
    if (this.#max !== void 0 && this.#filtered > this.#max) {
      this.#filtered = this.#max;
    }
    if (this.#min !== void 0 && this.#filtered < this.#min) {
      this.#filtered = this.#min;
    }
    return this.#filtered;
  }
  get filtered() {
    return this.#filtered;
  }
  get value() {
    return this.#filtered;
  }
  set alpha(alpha) {
    this.#alpha = alpha;
  }
  #validateAlpha(alpha) {
    if (alpha <= 0 || alpha > 1) {
      throw new Error("alpha must be in (0, 1].");
    }
  }
}
class AudioEnergyFilter {
  #cooldownSeconds;
  #cooldown;
  constructor(cooldownSeconds = 1) {
    this.#cooldownSeconds = cooldownSeconds;
    this.#cooldown = cooldownSeconds;
  }
  pushFrame(frame) {
    const arr = Float32Array.from(frame.data, (x) => x / 32768);
    const rms = (arr.map((x) => x ** 2).reduce((acc, x) => acc + x) / arr.length) ** 0.5;
    if (rms > 4e-3) {
      this.#cooldown = this.#cooldownSeconds;
      return true;
    }
    const durationSeconds = frame.samplesPerChannel / frame.sampleRate;
    this.#cooldown -= durationSeconds;
    if (this.#cooldown > 0) {
      return true;
    }
    return false;
  }
}
var TaskResult = /* @__PURE__ */ ((TaskResult2) => {
  TaskResult2["Timeout"] = "timeout";
  TaskResult2["Completed"] = "completed";
  TaskResult2["Aborted"] = "aborted";
  return TaskResult2;
})(TaskResult || {});
class Task {
  constructor(fn, controller, name) {
    this.fn = fn;
    this.controller = controller;
    this.name = name;
    this.resultFuture = new Future();
    void this.resultFuture.await.then(
      () => void 0,
      () => void 0
    ).finally(() => {
      for (const callback of this.doneCallbacks) {
        try {
          callback();
        } catch (error) {
          this.#logger.error({ error }, "Task done callback failed");
        }
      }
      this.doneCallbacks.clear();
    });
    this.runTask();
  }
  static currentTaskStorage = new import_node_async_hooks.AsyncLocalStorage();
  resultFuture;
  doneCallbacks = /* @__PURE__ */ new Set();
  #logger = (0, import_log.log)();
  /**
   * Creates a new task from a function.
   *
   * @param fn - The function to run
   * @param controller - The abort controller to use
   * @returns A new task
   */
  static from(fn, controller, name) {
    const abortController = controller ?? new AbortController();
    return new Task(fn, abortController, name);
  }
  /**
   * Returns the currently running task in this async context, if available.
   */
  static current() {
    return Task.currentTaskStorage.getStore();
  }
  async runTask() {
    const run = async () => {
      if (this.name) {
        this.#logger.debug(`Task.runTask: task ${this.name} started`);
      }
      return await this.fn(this.controller);
    };
    return Task.currentTaskStorage.run(this, run).then((value) => {
      this.resultFuture.resolve(value);
      return value;
    }).catch((error) => {
      this.resultFuture.reject(error);
    }).finally(() => {
      if (this.name) {
        this.#logger.debug(`Task.runTask: task ${this.name} done`);
      }
    });
  }
  /**
   * Cancels the task.
   */
  cancel() {
    this.controller.abort();
  }
  /**
   * Cancels the task and waits for it to complete.
   *
   * @param timeout - The timeout in milliseconds
   * @returns The result status of the task (timeout, completed, aborted)
   */
  async cancelAndWait(timeout) {
    this.cancel();
    const promises = [
      this.result.then(() => "completed" /* Completed */).catch((error) => {
        if (error.name === "AbortError") {
          return "aborted" /* Aborted */;
        }
        throw error;
      })
    ];
    if (timeout) {
      promises.push(delay(timeout).then(() => "timeout" /* Timeout */));
    }
    const result = await import_throws.ThrowsPromise.race(promises);
    if (result === "timeout" /* Timeout */) {
      throw new Error("Task cancellation timed out");
    }
    return result;
  }
  /**
   * The result of the task.
   */
  get result() {
    return this.resultFuture.await;
  }
  /**
   * Whether the task has completed.
   */
  get done() {
    return this.resultFuture.done;
  }
  addDoneCallback(callback) {
    if (this.done) {
      queueMicrotask(callback);
      return;
    }
    this.doneCallbacks.add(callback);
  }
  removeDoneCallback(callback) {
    this.doneCallbacks.delete(callback);
  }
}
async function waitFor(tasks) {
  await import_throws.ThrowsPromise.allSettled(tasks.map((task) => task.result));
}
async function cancelAndWait(tasks, timeout) {
  await import_throws.ThrowsPromise.allSettled(tasks.map((task) => task.cancelAndWait(timeout)));
}
function withResolvers() {
  let resolve;
  let reject;
  const promise = new import_throws.ThrowsPromise((res, rej) => {
    resolve = res;
    reject = rej;
  });
  return { promise, resolve, reject };
}
function shortuuid(prefix = "") {
  return `${prefix}${(0, import_node_crypto.randomUUID)().slice(0, 12)}`;
}
const READONLY_SYMBOL = Symbol("Readonly");
const MUTATION_METHODS = [
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "sort",
  "reverse",
  "fill",
  "copyWithin"
];
function createImmutableArray(array, additionalErrorMessage = "") {
  return new Proxy(array, {
    get(target, key) {
      if (key === READONLY_SYMBOL) {
        return true;
      }
      if (typeof key === "string" && MUTATION_METHODS.includes(key)) {
        return function() {
          throw new TypeError(
            `Cannot call ${key}() on a read-only array. ${additionalErrorMessage}`.trim()
          );
        };
      }
      return Reflect.get(target, key);
    },
    set(_, prop) {
      throw new TypeError(
        `Cannot assign to read-only array index "${String(prop)}". ${additionalErrorMessage}`.trim()
      );
    },
    deleteProperty(_, prop) {
      throw new TypeError(
        `Cannot delete read-only array index "${String(prop)}". ${additionalErrorMessage}`.trim()
      );
    },
    defineProperty(_, prop) {
      throw new TypeError(
        `Cannot define property "${String(prop)}" on a read-only array. ${additionalErrorMessage}`.trim()
      );
    },
    setPrototypeOf() {
      throw new TypeError(
        `Cannot change prototype of a read-only array. ${additionalErrorMessage}`.trim()
      );
    }
  });
}
function isImmutableArray(array) {
  return typeof array === "object" && !!array[READONLY_SYMBOL];
}
function resampleStream({
  stream,
  outputRate
}) {
  let resampler = null;
  let currentInputRate = 0;
  const transformStream = new import_web.TransformStream({
    transform(chunk, controller) {
      if (chunk.samplesPerChannel === 0) {
        controller.enqueue(chunk);
        return;
      }
      if (chunk.sampleRate === outputRate) {
        if (resampler) {
          for (const frame of resampler.flush()) {
            controller.enqueue(frame);
          }
          resampler.close();
          resampler = null;
        }
        controller.enqueue(chunk);
        return;
      }
      if (!resampler || currentInputRate !== chunk.sampleRate) {
        if (resampler) {
          for (const frame of resampler.flush()) {
            controller.enqueue(frame);
          }
          resampler.close();
        }
        resampler = new import_rtc_node.AudioResampler(chunk.sampleRate, outputRate);
        currentInputRate = chunk.sampleRate;
      }
      for (const frame of resampler.push(chunk)) {
        controller.enqueue(frame);
      }
    },
    flush(controller) {
      if (resampler) {
        for (const frame of resampler.flush()) {
          controller.enqueue(frame);
        }
        resampler.close();
        resampler = null;
      }
    }
  });
  return stream.pipeThrough(transformStream);
}
class InvalidErrorType extends Error {
  error;
  constructor(error) {
    super(`Expected error, got ${error} (${typeof error})`);
    this.error = error;
    Error.captureStackTrace(this, InvalidErrorType);
  }
}
function isStreamClosedError(error) {
  return error instanceof Error && (error.message === "Stream is closed" || error.message === "Input is closed");
}
function isWritableStreamClosedError(error) {
  if (!(error instanceof Error)) {
    return false;
  }
  if ("code" in error && error.code === "ERR_INVALID_STATE") {
    return true;
  }
  return error.message.includes("WritableStream is closed");
}
const FFMPEG_TEARDOWN_ERRORS = ["Output stream closed", "received signal 2", "SIGKILL", "SIGINT"];
function isFfmpegTeardownError(error) {
  return error instanceof Error && FFMPEG_TEARDOWN_ERRORS.some((msg) => {
    var _a;
    return (_a = error.message) == null ? void 0 : _a.includes(msg);
  });
}
function toError(error) {
  if (error instanceof Error) {
    return error;
  }
  throw new InvalidErrorType(error);
}
function startSoon(func) {
  setTimeout(func, 0);
}
function delay(ms, options = {}) {
  const { signal } = options;
  if (signal == null ? void 0 : signal.aborted) return import_throws.ThrowsPromise.reject(signal.reason ?? new Error("delay aborted"));
  return new import_throws.ThrowsPromise((resolve, reject) => {
    const abort = () => {
      clearTimeout(i);
      reject((signal == null ? void 0 : signal.reason) ?? new Error("delay aborted"));
    };
    const done = () => {
      signal == null ? void 0 : signal.removeEventListener("abort", abort);
      resolve();
    };
    const i = setTimeout(done, ms);
    signal == null ? void 0 : signal.addEventListener("abort", abort, { once: true });
  });
}
class IdleTimeoutError extends Error {
  constructor(message = "idle timeout") {
    super(message);
    this.name = "IdleTimeoutError";
  }
}
function waitUntilTimeout(promise, timeoutMs, throwError) {
  let timer;
  return import_throws.ThrowsPromise.race([
    promise,
    new import_throws.ThrowsPromise((_, reject) => {
      timer = setTimeout(() => reject((throwError == null ? void 0 : throwError()) ?? new IdleTimeoutError()), timeoutMs);
    })
  ]).finally(() => clearTimeout(timer));
}
async function waitForParticipant({
  room,
  identity,
  kind,
  includeLocal = false,
  signal
}) {
  if (!room.isConnected) {
    throw new Error("Room is not connected");
  }
  if (signal == null ? void 0 : signal.aborted) {
    throw new Error("waitForParticipant aborted");
  }
  const fut = new Future();
  const kindMatch = (participant) => {
    if (kind === void 0) return true;
    if (Array.isArray(kind)) {
      return kind.includes(participant.kind);
    }
    return participant.kind === kind;
  };
  const onParticipantConnected = (p) => {
    if ((identity === void 0 || p.identity === identity) && kindMatch(p)) {
      if (!fut.done) {
        fut.resolve(p);
      }
    }
  };
  const onDisconnected = () => {
    fut.reject(new Error("Got disconnected from room while waiting for participant"));
  };
  const onAbort = () => {
    if (!fut.done) {
      fut.reject(new Error("waitForParticipant aborted"));
    }
  };
  room.on(import_rtc_node.RoomEvent.ParticipantConnected, onParticipantConnected);
  room.on(import_rtc_node.RoomEvent.Disconnected, onDisconnected);
  signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
  try {
    const localParticipant = room.localParticipant;
    if (includeLocal && localParticipant && (identity === void 0 || localParticipant.identity === identity) && kindMatch(localParticipant)) {
      return localParticipant;
    }
    for (const p of room.remoteParticipants.values()) {
      onParticipantConnected(p);
      if (fut.done) {
        break;
      }
    }
    return await fut.await;
  } finally {
    room.off(import_rtc_node.RoomEvent.ParticipantConnected, onParticipantConnected);
    room.off(import_rtc_node.RoomEvent.Disconnected, onDisconnected);
    signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
  }
}
async function waitForParticipantAttribute({
  room,
  identity,
  attribute,
  value,
  signal
}) {
  if (!room.isConnected) {
    throw new Error("Room is not connected");
  }
  if (signal == null ? void 0 : signal.aborted) {
    throw new Error("waitForParticipantAttribute aborted");
  }
  const participant = room.remoteParticipants.get(identity);
  if (!participant) {
    throw new Error(`Participant ${identity} is not in the room`);
  }
  const fut = new Future();
  const isMatch = (p) => p instanceof import_rtc_node.RemoteParticipant && p.identity === identity && p.attributes[attribute] === value;
  const onParticipantAttributesChanged = (_changedAttributes, p) => {
    if (!fut.done && isMatch(p)) {
      fut.resolve();
    }
  };
  const onParticipantDisconnected = (p) => {
    if (!fut.done && p.identity === identity) {
      fut.reject(new Error(`Participant ${identity} disconnected while waiting for ${attribute}`));
    }
  };
  const onDisconnected = () => {
    if (!fut.done) {
      fut.reject(new Error(`Room disconnected while waiting for ${identity} ${attribute}`));
    }
  };
  const onAbort = () => {
    if (!fut.done) {
      fut.reject(new Error("waitForParticipantAttribute aborted"));
    }
  };
  room.on(import_rtc_node.RoomEvent.ParticipantAttributesChanged, onParticipantAttributesChanged);
  room.on(import_rtc_node.RoomEvent.ParticipantDisconnected, onParticipantDisconnected);
  room.on(import_rtc_node.RoomEvent.Disconnected, onDisconnected);
  signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
  try {
    const current = room.remoteParticipants.get(identity);
    if (!current) {
      throw new Error(`Participant ${identity} is not in the room`);
    }
    if (current.attributes[attribute] === value) {
      return;
    }
    await fut.await;
  } finally {
    room.off(import_rtc_node.RoomEvent.ParticipantAttributesChanged, onParticipantAttributesChanged);
    room.off(import_rtc_node.RoomEvent.ParticipantDisconnected, onParticipantDisconnected);
    room.off(import_rtc_node.RoomEvent.Disconnected, onDisconnected);
    signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
  }
}
async function waitForTrackPublication({
  room,
  identity,
  kind,
  waitForSubscription = false,
  signal,
  includeLocal = false
}) {
  if (!room.isConnected) {
    throw new Error("Room is not connected");
  }
  if (signal == null ? void 0 : signal.aborted) {
    throw new Error("waitForTrackPublication aborted");
  }
  const fut = new Future();
  const kindMatch = (k) => {
    if (kind === void 0 || kind === null) {
      return true;
    }
    return k === kind;
  };
  const matches = (publication, participant) => {
    if (identity !== void 0 && participant.identity !== identity) return false;
    if (!kindMatch(publication.kind)) return false;
    if (waitForSubscription && !(publication.subscribed && publication.track !== void 0)) {
      return false;
    }
    return true;
  };
  const onTrackPublished = (publication, participant) => {
    if (fut.done) return;
    if (matches(publication, participant)) {
      fut.resolve(publication);
    }
  };
  const onTrackSubscribed = (_track, publication, participant) => {
    if (fut.done) return;
    if (matches(publication, participant)) {
      fut.resolve(publication);
    }
  };
  const onLocalTrackPublished = (publication) => {
    if (fut.done || !publication) return;
    const localParticipant = room.localParticipant;
    if (localParticipant && (identity === void 0 || localParticipant.identity === identity)) {
      if (kindMatch(publication.kind)) {
        fut.resolve(publication);
      }
    }
  };
  if (waitForSubscription) {
    room.on(import_rtc_node.RoomEvent.TrackSubscribed, onTrackSubscribed);
  } else {
    room.on(import_rtc_node.RoomEvent.TrackPublished, onTrackPublished);
  }
  if (includeLocal) {
    room.on(import_rtc_node.RoomEvent.LocalTrackPublished, onLocalTrackPublished);
  }
  const onAbort = () => {
    if (!fut.done) {
      fut.reject(new Error("waitForTrackPublication aborted"));
    }
  };
  signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
  try {
    const localParticipant = room.localParticipant;
    if (includeLocal && localParticipant && (identity === void 0 || localParticipant.identity === identity)) {
      for (const publication of localParticipant.trackPublications.values()) {
        if (kindMatch(publication.kind)) {
          fut.resolve(publication);
          break;
        }
      }
    }
    for (const p of room.remoteParticipants.values()) {
      for (const publication of p.trackPublications.values()) {
        if (matches(publication, p)) {
          fut.resolve(publication);
          break;
        }
      }
      if (fut.done) break;
    }
    return await fut.await;
  } finally {
    if (waitForSubscription) {
      room.off(import_rtc_node.RoomEvent.TrackSubscribed, onTrackSubscribed);
    } else {
      room.off(import_rtc_node.RoomEvent.TrackPublished, onTrackPublished);
    }
    if (includeLocal) {
      room.off(import_rtc_node.RoomEvent.LocalTrackPublished, onLocalTrackPublished);
    }
    signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
  }
}
async function* readStream(stream, signal) {
  if (signal == null ? void 0 : signal.aborted) return;
  const reader = stream.getReader();
  try {
    if (signal) {
      const abortPromise = waitForAbort(signal);
      while (true) {
        const result = await import_throws.ThrowsPromise.race([reader.read(), abortPromise]);
        if (!result) break;
        const { done, value } = result;
        if (done) break;
        yield value;
      }
    } else {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        yield value;
      }
    }
  } finally {
    try {
      reader.releaseLock();
    } catch {
    }
  }
}
async function waitForAbort(signal) {
  if (signal.aborted) {
    return;
  }
  const abortFuture = new Future();
  const handler = () => {
    abortFuture.resolve();
    signal.removeEventListener("abort", handler);
  };
  if (signal.aborted) {
    return;
  }
  signal.addEventListener("abort", handler, { once: true });
  return await abortFuture.await;
}
async function rejectOnAbort(signal) {
  if (signal.aborted) throw signal.reason;
  const abortFuture = new Future();
  signal.addEventListener("abort", () => abortFuture.reject(signal.reason), { once: true });
  return abortFuture.await;
}
const combineSignals = (a, b) => {
  const c = new AbortController();
  const abortFrom = (s) => {
    if (c.signal.aborted) return;
    c.abort(s.reason);
  };
  if (a.aborted) {
    abortFrom(a);
  } else {
    a.addEventListener("abort", () => abortFrom(a), { once: true });
  }
  if (b.aborted) {
    abortFrom(b);
  } else {
    b.addEventListener("abort", () => abortFrom(b), { once: true });
  }
  return c.signal;
};
const isCloud = (url) => {
  const hostname = url.hostname;
  return hostname.endsWith(".livekit.cloud") || hostname.endsWith(".livekit.run");
};
const isDevMode = () => {
  return process.env.LIVEKIT_DEV_MODE === "1";
};
const isHosted = () => {
  return process.env.LIVEKIT_REMOTE_EOT_URL !== void 0;
};
function asError(maybeError) {
  if (maybeError instanceof Error) {
    return maybeError;
  }
  return new Error(String(maybeError));
}
function resolveEnvVar(value, envVars, defaultValue = "") {
  if (value !== void 0) {
    return value;
  }
  for (const name of envVars) {
    const v = process.env[name];
    if (v !== void 0 && v !== "") {
      return v;
    }
  }
  return defaultValue;
}
function dedent(strings, ...values) {
  let result = "";
  for (let i = 0; i < strings.length; i++) {
    result += strings[i];
    if (i < values.length) {
      result += String(values[i]);
    }
  }
  if (result.startsWith("\n")) {
    result = result.slice(1);
  }
  const lines = result.split("\n");
  let minIndent = Infinity;
  for (const line of lines) {
    if (line.trim().length === 0) continue;
    let spaces = 0;
    for (const ch of line) {
      if (ch === " " || ch === "	") {
        spaces++;
      } else {
        break;
      }
    }
    minIndent = Math.min(minIndent, spaces);
  }
  if (minIndent === Infinity) {
    minIndent = 0;
  }
  return lines.map((line) => line.slice(minIndent)).join("\n").trimEnd();
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  AsyncIterableQueue,
  AudioEnergyFilter,
  CancellablePromise,
  Event,
  ExpFilter,
  Future,
  IdleTimeoutError,
  InvalidErrorType,
  Queue,
  Task,
  TaskResult,
  asError,
  cancelAndWait,
  combineSignals,
  createImmutableArray,
  dedent,
  delay,
  gracefullyCancel,
  isCloud,
  isDevMode,
  isFfmpegTeardownError,
  isHosted,
  isImmutableArray,
  isPending,
  isStreamClosedError,
  isWritableStreamClosedError,
  mergeFrames,
  noop,
  readStream,
  rejectOnAbort,
  resampleStream,
  resolveEnvVar,
  shortuuid,
  startSoon,
  toError,
  waitFor,
  waitForAbort,
  waitForParticipant,
  waitForParticipantAttribute,
  waitForTrackPublication,
  waitUntilTimeout,
  withResolvers
});
//# sourceMappingURL=utils.cjs.map