{"version":3,"file":"pLimit.mjs","names":["#head","#tail","#size"],"sources":["../../../src/utils/pLimit.ts"],"sourcesContent":["/*\nHow it works:\n`this.#head` is an instance of `Node` which keeps track of its current value and nests another instance of `Node` that keeps the value that comes after it. When a value is provided to `.enqueue()`, the code needs to iterate through `this.#head`, going deeper and deeper to find the last value. However, iterating through every single item is slow. This problem is solved by saving a reference to the last value as `this.#tail` so that it can reference it to add a new value.\n*/\n\nclass Node<T> {\n  value: T;\n  next: Node<T> | undefined;\n\n  constructor(value: T) {\n    this.value = value;\n  }\n}\n\nexport class Queue<T> {\n  #head: Node<T> | undefined;\n  #tail: Node<T> | undefined;\n  #size!: number;\n\n  constructor() {\n    this.clear();\n  }\n\n  enqueue(value: T): void {\n    const node = new Node(value);\n\n    if (this.#head) {\n      this.#tail!.next = node;\n      this.#tail = node;\n    } else {\n      this.#head = node;\n      this.#tail = node;\n    }\n\n    this.#size++;\n  }\n\n  dequeue(): T | undefined {\n    const current = this.#head;\n    if (!current) {\n      return;\n    }\n\n    this.#head = current.next;\n    this.#size--;\n    return current.value;\n  }\n\n  peek(): T | undefined {\n    if (!this.#head) {\n      return;\n    }\n\n    return this.#head.value;\n\n    // TODO: Node.js 18.\n    // return this.#head?.value;\n  }\n\n  clear(): void {\n    this.#head = undefined;\n    this.#tail = undefined;\n    this.#size = 0;\n  }\n\n  get size(): number {\n    return this.#size;\n  }\n\n  *[Symbol.iterator](): Generator<T, void, unknown> {\n    let current = this.#head;\n\n    while (current) {\n      yield current.value;\n      current = current.next;\n    }\n  }\n\n  *drain(): Generator<T | undefined, void, unknown> {\n    while (this.#head) {\n      yield this.dequeue();\n    }\n  }\n}\n\nexport const pLimit = (concurrency: number) => {\n  validateConcurrency(concurrency);\n\n  const queue = new Queue<() => void>();\n  let activeCount = 0;\n\n  const idleWaiters: Array<() => void> = [];\n  const notifyIdleIfNeeded = (): void => {\n    if (activeCount === 0 && queue.size === 0) {\n      while (idleWaiters.length) idleWaiters.pop()?.();\n    }\n  };\n\n  const resumeNext = (): void => {\n    // Process the next queued function if we're under the concurrency limit\n    if (activeCount < concurrency && queue.size > 0) {\n      activeCount++;\n      queue.dequeue()?.();\n    }\n  };\n\n  const next = (): void => {\n    activeCount--;\n    resumeNext();\n  };\n\n  const run = async (\n    fn: (...args: any[]) => Promise<any>,\n    resolve: (value: Promise<any>) => void,\n    arguments_: any[]\n  ): Promise<void> => {\n    // Execute the function and capture the result promise\n    const result = (async () => fn(...arguments_))();\n\n    // Resolve immediately with the promise (don't wait for completion)\n    resolve(result);\n\n    // Wait for the function to complete (success or failure)\n    // We catch errors here to prevent unhandled rejections,\n    // but the original promise rejection is preserved for the caller\n    try {\n      await result;\n    } catch {}\n\n    // Decrement active count and process next queued function\n    next();\n  };\n\n  const enqueue = (\n    fn: (...args: any[]) => Promise<any>,\n    resolve: (value: Promise<any>) => void,\n    arguments_: any[]\n  ): void => {\n    // Queue the internal resolve function instead of the run function\n    // to preserve the asynchronous execution context.\n    new Promise<void>((internalResolve) => {\n      queue.enqueue(internalResolve);\n    }).then(run.bind(undefined, fn, resolve, arguments_));\n\n    // Start processing immediately if we haven't reached the concurrency limit\n    if (activeCount < concurrency) {\n      resumeNext();\n    }\n  };\n\n  const generator = (\n    fn: (...args: any[]) => Promise<any>,\n    ...arguments_: any[]\n  ): Promise<any> =>\n    new Promise<any>((resolve) => {\n      enqueue(fn, resolve, arguments_);\n    });\n\n  Object.defineProperties(generator, {\n    activeCount: {\n      get: () => activeCount,\n    },\n    pendingCount: {\n      get: () => queue.size,\n    },\n    clearQueue: {\n      value() {\n        queue.clear();\n        notifyIdleIfNeeded();\n      },\n    },\n    concurrency: {\n      get: () => concurrency,\n\n      set(newConcurrency: number) {\n        validateConcurrency(newConcurrency);\n        concurrency = newConcurrency;\n\n        queueMicrotask(() => {\n          while (activeCount < concurrency && queue.size > 0) {\n            resumeNext();\n          }\n        });\n      },\n    },\n    map: {\n      async value<T, R>(\n        array: T[],\n        fn: (value: T, index: number) => Promise<R>\n      ): Promise<R[]> {\n        const promises = array.map((value, index) => this(fn, value, index));\n        return Promise.all(promises);\n      },\n    },\n    onIdle: {\n      /**\n       * Resolves when `activeCount === 0` and the queue is empty.\n       * Use this to wait for completion without holding a list of Promises.\n       */\n      value(): Promise<void> {\n        if (activeCount === 0 && queue.size === 0) return Promise.resolve();\n        return new Promise<void>((resolve) => idleWaiters.push(resolve));\n      },\n    },\n  });\n\n  return generator;\n};\n\nconst validateConcurrency = (concurrency: number): void => {\n  if (\n    !(\n      (Number.isInteger(concurrency) ||\n        concurrency === Number.POSITIVE_INFINITY) &&\n      concurrency > 0\n    )\n  ) {\n    throw new TypeError('Expected `concurrency` to be a number from 1 and up');\n  }\n};\n"],"mappings":"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"}