ChemPal Documentation - v0.0.13-beta.5
    Preparing search index...

    Function fetchDecorator

    • A decorator function that wraps the native fetch API with additional features:

      • Automatic response parsing based on content type
      • Request hash generation for tracking
      • Error handling
      • Response cloning to prevent body stream consumption

      Parameters

      • input: URL | RequestInfo

        The request URL or Request object

      • Optionalinit: RequestInit

        Optional request configuration

      Returns Promise<FetchDecoratorResponse>

      A promise that resolves to a FetchDecoratorResponse

      Error if the response is not ok (status not in 200-299 range)

      // Basic GET request
      const response = await fetchDecorator("https://api.example.com/data");
      console.log(response.data); // Parsed response data
      console.log(response.requestHash); // Unique request hash

      // POST request with JSON body
      const response = await fetchDecorator("https://api.example.com/data", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name: "test" })
      });

      // Using Request object
      const request = new Request("https://api.example.com/data", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name: "test" })
      });
      const response = await fetchDecorator(request);

      // Handling different response types
      const response = await fetchDecorator("https://api.example.com/data");
      if (response.headers.get("content-type")?.includes("application/json")) {
      // Data is already parsed as JSON
      console.log(response.data);
      } else if (response.headers.get("content-type")?.includes("text/")) {
      // Data is already parsed as text
      console.log(response.data);
      } else {
      // Data is a Blob
      const blob = response.data as Blob;
      // Handle blob data
      }
      export async function fetchDecorator(
      input: RequestInfo | URL,
      init?: RequestInit,
      ): Promise<FetchDecoratorResponse> {
      const requestHash = await generateRequestHash(input, init);
      console.debug(`Request Hash: ${requestHash}`);

      // Clone the request for aggregate capture BEFORE fetch() consumes it.
      // For POST requests, fetch() reads the request body, making it impossible
      // to clone afterward.
      let aggregateRequestClone: Request | undefined;
      if (typeof __RESPONSE_AGGREGATE__ !== "undefined" && __RESPONSE_AGGREGATE__) {
      aggregateRequestClone =
      input instanceof Request ? input.clone() : new Request(input.toString(), init);
      }

      const response = await fetch(input, init);
      // So we can return the original response
      const clonedResponse = response.clone();

      // Clone the response for aggregate capture BEFORE the body is transferred
      // to enhancedResponse (which makes the original response disturbed).
      // Done before the !response.ok check so error responses (4xx, 5xx) are also captured.
      let aggregateResponseClone: Response | undefined;
      if (typeof __RESPONSE_AGGREGATE__ !== "undefined" && __RESPONSE_AGGREGATE__) {
      aggregateResponseClone = response.clone();
      }

      if (!response.ok) {
      // Still capture error responses for mocking in tests
      if (aggregateRequestClone && aggregateResponseClone) {
      await addCapturedResponse(aggregateRequestClone, aggregateResponseClone);
      }
      throw new HttpError(clonedResponse.status, clonedResponse.statusText);
      }

      const contentType = clonedResponse.headers.get("content-type") || "";
      let data: unknown;

      try {
      if (contentType.includes("application/json")) {
      data = await clonedResponse.clone().json();
      } else if (contentType.includes("text/") || contentType.includes("json-amazonui-streaming")) {
      data = await clonedResponse.clone().text();
      } else {
      data = await clonedResponse.clone().blob();
      }
      } catch {
      console.debug("clonedResponse:", clonedResponse);
      data = await clonedResponse.clone().text();
      }

      // Create a new Response object that inherits all prototype methods
      const enhancedResponse = new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers: response.headers,
      });

      // Add our custom properties
      Object.defineProperties(enhancedResponse, {
      data: { value: data },
      requestHash: { value: requestHash },
      });

      // Capture response when in aggregate mode.
      // Awaited to ensure the capture completes before the clones are GC'd.
      if (aggregateRequestClone && aggregateResponseClone) {
      await addCapturedResponse(aggregateRequestClone, aggregateResponseClone);
      }

      return enhancedResponse as FetchDecoratorResponse;
      }