{"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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"}