// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

/**
 * Concatenates multiple `ReadableStream`s into a single ordered
 * `ReadableStream`.
 *
 * Cancelling the resulting stream will cancel all the input streams.
 *
 * @typeParam T The type of the chunks in the streams.
 * @param streams An iterable of `ReadableStream`s to concat.
 * @returns A `ReadableStream` that will emit the concatenated chunks.
 *
 * @example Usage
 * ```ts
 * import { concatReadableStreams } from "@std/streams/concat-readable-streams";
 * import { assertEquals } from "@std/assert/assert-equals";
 *
 * const stream1 = ReadableStream.from([1, 2, 3]);
 * const stream2 = ReadableStream.from([4, 5, 6]);
 * const stream3 = ReadableStream.from([7, 8, 9]);
 *
 * assertEquals(
 *   await Array.fromAsync(concatReadableStreams(stream1, stream2, stream3)),
 *   [1, 2, 3, 4, 5, 6, 7, 8, 9],
 * );
 * ```
 */
export function concatReadableStreams<T>(
  ...streams: ReadableStream<T>[]
): ReadableStream<T> {
  let i = 0;
  return new ReadableStream<T>({
    async pull(controller) {
      const reader = streams[i]!.getReader();
      const { done, value } = await reader.read();
      if (done) {
        if (streams.length === ++i) {
          return controller.close();
        }
        return await this.pull!(controller);
      }
      controller.enqueue(value);
      reader.releaseLock();
    },
    async cancel(reason) {
      const promises = streams.map((stream) => stream.cancel(reason));
      await Promise.allSettled(promises);
    },
  });
}

// denoCacheMetadata={"headers":{"date":"Tue, 28 Jul 2026 12:31:15 GMT","content-security-policy":"default-src 'none'; script-src 'none'; style-src 'none'; img-src 'none'; font-src 'none'; connect-src 'none'; frame-src 'none'; object-src 'none'; frame-ancestors 'none'; sandbox; form-action 'none';","cf-cache-status":"HIT","access-control-allow-origin":"*","content-type":"text/typescript","cf-ray":"a223e657caa47db6-LAX","accept-ranges":"bytes","x-content-type-options":"nosniff","cache-control":"public, max-age=31536000, immutable","content-length":"1557","x-jsr-backend":"modules","etag":"\"a2499af4ba80a0405a2c1f0d94c95359\"","x-robots-tag":"noindex","report-to":"{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=Lkjf4IHT7u3%2FxNEP8Sx8CfsfRCzQNOtHqBg4OCJoGCUsp5l1e3CoxLCfsavWEKZUGQhipUxjKPzSl4l3LYtslaXmWfyxMRwOffSURk24d7fByr5dII5oN3Q%3D\"}]}","vary":"Origin","alt-svc":"h3=\":443\"; ma=86400","nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}","server":"cloudflare","set-cookie":"__cf_bm=yRo2gJzbm2rqfT8WrWL5EzGPJMVHdRMBWO0fBC.7zk8-1785241875.1754131-1.0.1.1-Ic5O9II4u9XZoEVQavuq46BY4mu0FZF5pNffOH.ySy3uvAKcUI9ENk731GFrTzEUMF5mA8KuVNH7BAzxe6xdC4cXcDSb5BBX8lLisWLYGZ1HK0QlfCA_SpW0_qoR2Rlb; HttpOnly; SameSite=None; Secure; Path=/; Domain=jsr.io; Expires=Tue, 28 Jul 2026 13:01:15 GMT","age":"112265","cross-origin-resource-policy":"cross-origin","access-control-expose-headers":"*"},"url":"https://jsr.io/@std/streams/0.224.5/concat_readable_streams.ts","time":1785241875}