// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

/** Options for {@linkcode LimitedTransformStream} */
export interface LimitedTransformStreamOptions {
  /**
   * If true, a {@linkcode RangeError} is thrown when the total number of
   * enqueued chunks is about to exceed the specified limit.
   *
   * @default {false}
   */
  error?: boolean;
}

/**
 * A {@linkcode TransformStream} that will only read & enqueue `size` amount of
 * chunks.
 *
 * If `options.error` is set, then instead of terminating the stream,
 * a {@linkcode RangeError} will be thrown when the total number of enqueued
 * chunks is about to exceed the specified size.
 *
 * @typeparam T The type the chunks in the stream.
 *
 * @example `size` is equal to the total number of chunks
 * ```ts
 * import { LimitedTransformStream } from "@std/streams/limited-transform-stream";
 * import { assertEquals } from "@std/assert/assert-equals";
 *
 * const stream = ReadableStream.from(["1234", "5678"]);
 * const transformed = stream.pipeThrough(
 *   new LimitedTransformStream(2),
 * );
 *
 * // All chunks were read
 * assertEquals(
 *   await Array.fromAsync(transformed),
 *   ["1234", "5678"],
 * );
 * ```
 *
 * @example `size` is less than the total number of chunks
 * ```ts
 * import { LimitedTransformStream } from "@std/streams/limited-transform-stream";
 * import { assertEquals } from "@std/assert/assert-equals";
 *
 * const stream = ReadableStream.from(["1234", "5678"]);
 * const transformed = stream.pipeThrough(
 *   new LimitedTransformStream(1),
 * );
 *
 * // Only the first chunk was read
 * assertEquals(
 *   await Array.fromAsync(transformed),
 *   ["1234"],
 * );
 * ```
 *
 * @example error: true
 * ```ts
 * import { LimitedTransformStream } from "@std/streams/limited-transform-stream";
 * import { assertRejects } from "@std/assert/assert-rejects";
 *
 * const stream = ReadableStream.from(["1234", "5678"]);
 * const transformed = stream.pipeThrough(
 *   new LimitedTransformStream(1, { error: true }),
 * );
 *
 * await assertRejects(async () => {
 *   await Array.fromAsync(transformed);
 * }, RangeError);
 * ```
 */
export class LimitedTransformStream<T> extends TransformStream<T, T> {
  #read = 0;

  /**
   * Constructs a new instance.
   *
   * @param size The maximum number of chunks to read.
   * @param options Options for the stream.
   *
   * @example size = 42
   * ```ts no-assert
   * import { LimitedTransformStream } from "@std/streams/limited-transform-stream";
   *
   * const limitedTransformStream = new LimitedTransformStream(42);
   * ```
   *
   * @example size = 42, error = true
   * ```ts no-assert
   * import { LimitedTransformStream } from "@std/streams/limited-transform-stream";
   *
   * const limitedTransformStream = new LimitedTransformStream(42, { error: true });
   * ```
   */
  constructor(
    size: number,
    options: LimitedTransformStreamOptions = { error: false },
  ) {
    super({
      transform: (chunk, controller) => {
        if ((this.#read + 1) > size) {
          if (options.error) {
            throw new RangeError(`Exceeded chunk limit of '${size}'`);
          } else {
            controller.terminate();
          }
        } else {
          this.#read++;
          controller.enqueue(chunk);
        }
      },
    });
  }
}

// denoCacheMetadata={"headers":{"content-type":"text/typescript","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';","x-jsr-backend":"modules","content-length":"3357","set-cookie":"__cf_bm=vHbOCHStQ_xixhh.G8HJfyRzTQTccC0ZEMBIopzkoYg-1785241875.1805177-1.0.1.1-Tw9.71lu9L2piXjCrrkGho8ti53kmxVVGo7ESCas85rp4WrmS0QbPClkm5_f3Wne3WmjIKNpJQF.eMA3gMwiXQE.8yjtY6akHCCeNofxoZf1zi0ggoNAoua824G7gHv8; HttpOnly; SameSite=None; Secure; Path=/; Domain=jsr.io; Expires=Tue, 28 Jul 2026 13:01:15 GMT","cache-control":"public, max-age=31536000, immutable","etag":"\"aba66a2223ec0b1a42aa26b5acdf55be\"","x-content-type-options":"nosniff","report-to":"{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=FPyTreMwkQt8SGZjVxi4u%2Bu7rTTX1y1jcMmjoNZQWijhmFgFwkMafxvc4Dwm0JtMLuHva6ikOdGlBfrZ8Bpwxfco%2FWqYhnjncIlsiWm1sSmEIpFN1AXkJqs%3D\"}]}","nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}","access-control-allow-origin":"*","cf-ray":"a223e657dab67db6-LAX","accept-ranges":"bytes","date":"Tue, 28 Jul 2026 12:31:15 GMT","age":"112265","cross-origin-resource-policy":"cross-origin","access-control-expose-headers":"*","alt-svc":"h3=\":443\"; ma=86400","cf-cache-status":"HIT","x-robots-tag":"noindex","server":"cloudflare","vary":"Origin"},"url":"https://jsr.io/@std/streams/0.224.5/limited_transform_stream.ts","time":1785241875}