@flow-conductor/core 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.d.ts CHANGED
@@ -573,6 +573,244 @@ declare class RequestChain<Out, AdapterExecutionResult = Out, AdapterRequestConf
573
573
  */
574
574
  declare function begin<Out, AdapterExecutionResult, AdapterRequestConfig extends IRequestConfig = IRequestConfig>(stage: PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig> | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>, adapter: RequestAdapter<AdapterExecutionResult, AdapterRequestConfig>): RequestChain<Out, AdapterExecutionResult, AdapterRequestConfig, [Out]>;
575
575
 
576
+ /**
577
+ * Type helper to extract the output type from a pipeline stage.
578
+ */
579
+ type ExtractStageOutput<Stage extends PipelineRequestStage<any, any, any> | PipelineManagerStage<any, any, any>> = Stage extends PipelineRequestStage<any, infer Out, any> ? Out : Stage extends PipelineManagerStage<infer Out, any, any> ? Out : never;
580
+ /**
581
+ * Type helper to convert an array of stages into a tuple of their output types.
582
+ * This enables heterogeneous batch requests where each request can return a different type.
583
+ */
584
+ type StagesToTuple<Stages extends readonly (PipelineRequestStage<any, any, any> | PipelineManagerStage<any, any, any>)[]> = {
585
+ readonly [K in keyof Stages]: ExtractStageOutput<Stages[K]>;
586
+ };
587
+ /**
588
+ * A batch request manager that executes multiple requests in parallel (or with a concurrency limit).
589
+ * All requests are executed simultaneously (or in controlled batches), and results are returned as an array or tuple.
590
+ *
591
+ * Supports both homogeneous batches (all requests return the same type) and heterogeneous batches
592
+ * (each request can return a different type, using tuple types for type safety).
593
+ *
594
+ * @template Out - The output type:
595
+ * - For homogeneous batches: an array type (e.g., `User[]`)
596
+ * - For heterogeneous batches: a tuple type (e.g., `[User, Product, Order]`)
597
+ * @template AdapterExecutionResult - The type of result returned by the adapter
598
+ * @template RequestConfig - The type of request configuration
599
+ *
600
+ * @example
601
+ * ```typescript
602
+ * // Homogeneous batch - each request returns a User
603
+ * const batch = new RequestBatch<User[], Response, FetchRequestConfig>();
604
+ * batch.setRequestAdapter(adapter);
605
+ * batch.addAll([
606
+ * { config: { url: '/api/users/1', method: 'GET' } },
607
+ * { config: { url: '/api/users/2', method: 'GET' } },
608
+ * { config: { url: '/api/users/3', method: 'GET' } }
609
+ * ]);
610
+ * const results: User[] = await batch.execute();
611
+ *
612
+ * // Heterogeneous batch - each request returns a different type
613
+ * const heterogeneousBatch = RequestBatch.batch(
614
+ * [
615
+ * { config: { url: '/api/users/1', method: 'GET' }, mapper: (r) => r.json() as Promise<User> },
616
+ * { config: { url: '/api/products/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Product> },
617
+ * { config: { url: '/api/orders/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Order> }
618
+ * ],
619
+ * adapter
620
+ * );
621
+ * const results: [User, Product, Order] = await heterogeneousBatch.execute();
622
+ * ```
623
+ */
624
+ declare class RequestBatch<Out, AdapterExecutionResult = Out, RequestConfig extends IRequestConfig = IRequestConfig> extends RequestFlow<Out, AdapterExecutionResult, RequestConfig> {
625
+ /**
626
+ * Creates a new RequestBatch with stages and adapter.
627
+ * This is the entry point for building a request batch.
628
+ *
629
+ * Supports both homogeneous batches (all requests return the same type) and
630
+ * heterogeneous batches (each request can return a different type, using tuple types).
631
+ *
632
+ * @template Out - The output type (should be an array type, e.g., `User[]` for homogeneous batches)
633
+ * @template AdapterExecutionResult - The type of result returned by the adapter
634
+ * @template RequestConfig - The type of request configuration
635
+ * @param stages - Array of pipeline stages (request or manager stages)
636
+ * @param adapter - The request adapter to use for HTTP requests
637
+ * @returns A new RequestBatch instance with the stages and adapter configured
638
+ *
639
+ * @example
640
+ * ```typescript
641
+ * // Homogeneous batch - all requests return User
642
+ * const batch = RequestBatch.batch(
643
+ * [
644
+ * { config: { url: '/api/users/1', method: 'GET' } },
645
+ * { config: { url: '/api/users/2', method: 'GET' } },
646
+ * { config: { url: '/api/users/3', method: 'GET' } }
647
+ * ],
648
+ * adapter
649
+ * );
650
+ * const results: User[] = await batch.execute();
651
+ *
652
+ * // Heterogeneous batch - each request returns a different type
653
+ * const heterogeneousBatch = RequestBatch.batch(
654
+ * [
655
+ * { config: { url: '/api/users/1', method: 'GET' }, mapper: (r) => r.json() as Promise<User> },
656
+ * { config: { url: '/api/products/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Product> },
657
+ * { config: { url: '/api/orders/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Order> }
658
+ * ],
659
+ * adapter
660
+ * );
661
+ * const results: [User, Product, Order] = await heterogeneousBatch.execute();
662
+ * ```
663
+ */
664
+ static batch: <Stages extends readonly (PipelineRequestStage<AdapterExecutionResult_1, any, RequestConfig_1> | PipelineManagerStage<any, AdapterExecutionResult_1, RequestConfig_1>)[], AdapterExecutionResult_1, RequestConfig_1 extends IRequestConfig = IRequestConfig>(stages: Stages, adapter: RequestAdapter<AdapterExecutionResult_1, RequestConfig_1>) => RequestBatch<StagesToTuple<Stages>, AdapterExecutionResult_1, RequestConfig_1>;
665
+ /**
666
+ * Maximum number of concurrent requests to execute at the same time.
667
+ * If undefined, all requests will execute in parallel (default behavior).
668
+ */
669
+ private concurrency?;
670
+ /**
671
+ * Sets the maximum number of concurrent requests to execute simultaneously.
672
+ * If not set, all requests will execute in parallel.
673
+ *
674
+ * @param limit - Maximum number of concurrent requests (must be > 0)
675
+ * @returns The current RequestBatch instance for method chaining
676
+ * @throws {Error} If limit is less than or equal to 0
677
+ *
678
+ * @example
679
+ * ```typescript
680
+ * const batch = new RequestBatch<User[], Response, FetchRequestConfig>();
681
+ * batch.setRequestAdapter(adapter);
682
+ * batch.withConcurrency(5); // Execute max 5 requests at a time
683
+ * batch.addAll([...requests]);
684
+ * const results = await batch.execute();
685
+ * ```
686
+ */
687
+ withConcurrency(limit: number): RequestBatch<Out, AdapterExecutionResult, RequestConfig>;
688
+ /**
689
+ * Executes all requests in the batch in parallel (or with concurrency limit if set) and returns all results.
690
+ * Handles errors and calls registered handlers appropriately.
691
+ *
692
+ * For homogeneous batches, `Out` should be an array type (e.g., `User[]`).
693
+ * For heterogeneous batches, `Out` should be a tuple type (e.g., `[User, Product, Order]`).
694
+ *
695
+ * @returns A promise that resolves to an array or tuple of all results (typed as Out)
696
+ * @throws {Error} If an error occurs and no error handler is registered
697
+ */
698
+ execute: () => Promise<Out>;
699
+ /**
700
+ * Executes all request entities in parallel (or with concurrency limit if set), handling preconditions and mappers.
701
+ * Stages with failed preconditions are skipped.
702
+ * This method preserves individual types for each stage, enabling heterogeneous batches.
703
+ *
704
+ * @param requestEntityList - List of pipeline stages to execute
705
+ * @returns A promise that resolves to an array of all stage results (preserves tuple types)
706
+ */
707
+ private executeAllRequestsHeterogeneous;
708
+ /**
709
+ * Executes promise factories with a concurrency limit.
710
+ * Processes promises in batches, ensuring only a limited number run concurrently.
711
+ * This version preserves index information for tuple type support.
712
+ *
713
+ * @param promiseFactories - Array of functions that return promises when called
714
+ * @param limit - Maximum number of concurrent promises to execute
715
+ * @returns A promise that resolves to an array of results with index information
716
+ */
717
+ private executeWithConcurrencyLimitHeterogeneous;
718
+ /**
719
+ * Executes a single request entity (stage).
720
+ * Handles both request stages and nested manager stages.
721
+ * This version supports heterogeneous types by inferring the type from the stage.
722
+ *
723
+ * @param requestEntity - The pipeline stage to execute
724
+ * @param previousResult - The result from the previous stage (optional, not used in batch)
725
+ * @returns A promise that resolves to the stage result (type inferred from stage)
726
+ * @throws {Error} If the stage type is unknown or all retries are exhausted
727
+ */
728
+ private executeSingleHeterogeneous;
729
+ /**
730
+ * Executes a request with retry logic based on the provided retry configuration.
731
+ * Supports chunk processing for streaming responses.
732
+ *
733
+ * @template Out - The output type
734
+ * @param requestConfig - The request configuration
735
+ * @param retryConfig - The retry configuration
736
+ * @param chunkProcessing - Optional chunk processing configuration
737
+ * @returns A promise that resolves to the request result
738
+ * @throws {Error} If all retry attempts are exhausted
739
+ */
740
+ private executeWithRetry;
741
+ /**
742
+ * Processes a result with chunk processing if enabled.
743
+ * Handles streaming responses by processing chunks progressively.
744
+ *
745
+ * @template Out - The output type
746
+ * @param rawResult - The raw result from the adapter
747
+ * @param chunkProcessing - Optional chunk processing configuration
748
+ * @returns A promise that resolves to the processed result
749
+ */
750
+ private processResultWithChunks;
751
+ /**
752
+ * Calculates the delay before the next retry attempt.
753
+ *
754
+ * @param attempt - The current attempt number (1-indexed for retries)
755
+ * @param error - The error that occurred
756
+ * @param retryConfig - The retry configuration
757
+ * @returns The delay in milliseconds
758
+ */
759
+ private calculateRetryDelay;
760
+ /**
761
+ * Sleeps for the specified number of milliseconds.
762
+ *
763
+ * @param ms - Milliseconds to sleep
764
+ * @returns A promise that resolves after the delay
765
+ */
766
+ private sleep;
767
+ }
768
+ /**
769
+ * Creates a new RequestBatch with stages and adapter.
770
+ * This is a convenience function that wraps RequestBatch.batch().
771
+ *
772
+ * Supports both homogeneous batches (all requests return the same type) and
773
+ * heterogeneous batches (each request can return a different type, using tuple types).
774
+ *
775
+ * @template Stages - The tuple of stages (inferred from the stages parameter)
776
+ * @template AdapterExecutionResult - The type of result returned by the adapter
777
+ * @template RequestConfig - The type of request configuration
778
+ * @param stages - Array or tuple of pipeline stages (request or manager stages)
779
+ * @param adapter - The request adapter to use for HTTP requests
780
+ * @returns A new RequestBatch instance with the stages and adapter configured
781
+ *
782
+ * @example
783
+ * ```typescript
784
+ * import { batch } from '@flow-conductor/core';
785
+ * import { FetchRequestAdapter } from '@flow-conductor/adapter-fetch';
786
+ *
787
+ * const adapter = new FetchRequestAdapter();
788
+ *
789
+ * // Homogeneous batch - all requests return User
790
+ * const batchInstance = batch(
791
+ * [
792
+ * { config: { url: '/api/users/1', method: 'GET' } },
793
+ * { config: { url: '/api/users/2', method: 'GET' } },
794
+ * { config: { url: '/api/users/3', method: 'GET' } }
795
+ * ],
796
+ * adapter
797
+ * );
798
+ * const results: User[] = await batchInstance.execute();
799
+ *
800
+ * // Heterogeneous batch - each request returns a different type
801
+ * const heterogeneousBatch = batch(
802
+ * [
803
+ * { config: { url: '/api/users/1', method: 'GET' }, mapper: (r) => r.json() as Promise<User> },
804
+ * { config: { url: '/api/products/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Product> },
805
+ * { config: { url: '/api/orders/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Order> }
806
+ * ],
807
+ * adapter
808
+ * );
809
+ * const results: [User, Product, Order] = await heterogeneousBatch.execute();
810
+ * ```
811
+ */
812
+ declare function batch<Stages extends readonly (PipelineRequestStage<AdapterExecutionResult, any, RequestConfig> | PipelineManagerStage<any, AdapterExecutionResult, RequestConfig>)[], AdapterExecutionResult, RequestConfig extends IRequestConfig = IRequestConfig>(stages: Stages, adapter: RequestAdapter<AdapterExecutionResult, RequestConfig>): RequestBatch<StagesToTuple<Stages>, AdapterExecutionResult, RequestConfig>;
813
+
576
814
  /**
577
815
  * Utility functions for retry logic and error handling.
578
816
  */
@@ -671,4 +909,4 @@ declare function isReadableStream(value: unknown): value is ReadableStream<Uint8
671
909
  */
672
910
  declare function hasReadableStream(response: Response): boolean;
673
911
 
674
- export { type BasePipelineStage, type ChunkHandler, type ChunkProcessingConfig, type ErrorHandler, type IRequestConfig, type IRequestConfigFactory, type PipelineManagerStage, type PipelineRequestStage, RequestAdapter, RequestChain, RequestFlow as RequestManager, type ResultHandler, type RetryConfig, SSRFError, type UrlValidationOptions, begin, RequestChain as default, defaultRetryCondition, getErrorStatus, hasReadableStream, isNetworkError, isReadableStream, processResponseStream, processStream, processTextStreamLineByLine, retryOnNetworkOrStatusCodes, retryOnStatusCodes, validateUrl };
912
+ export { type BasePipelineStage, type ChunkHandler, type ChunkProcessingConfig, type ErrorHandler, type IRequestConfig, type IRequestConfigFactory, type PipelineManagerStage, type PipelineRequestStage, RequestAdapter, RequestBatch, RequestChain, RequestFlow as RequestManager, type ResultHandler, type RetryConfig, SSRFError, type UrlValidationOptions, batch, begin, RequestChain as default, defaultRetryCondition, getErrorStatus, hasReadableStream, isNetworkError, isReadableStream, processResponseStream, processStream, processTextStreamLineByLine, retryOnNetworkOrStatusCodes, retryOnStatusCodes, validateUrl };
package/build/index.js CHANGED
@@ -662,6 +662,361 @@ function isPipelineManagerStage(stage) {
662
662
  return "request" in stage && !("config" in stage);
663
663
  }
664
664
 
665
- export { RequestAdapter, RequestChain, RequestFlow as RequestManager, SSRFError, begin, RequestChain as default, defaultRetryCondition, getErrorStatus, hasReadableStream, isNetworkError, isReadableStream, processResponseStream, processStream, processTextStreamLineByLine, retryOnNetworkOrStatusCodes, retryOnStatusCodes, validateUrl };
665
+ // src/request-batch.ts
666
+ var _RequestBatch = class _RequestBatch extends RequestFlow {
667
+ constructor() {
668
+ super(...arguments);
669
+ /**
670
+ * Executes all requests in the batch in parallel (or with concurrency limit if set) and returns all results.
671
+ * Handles errors and calls registered handlers appropriately.
672
+ *
673
+ * For homogeneous batches, `Out` should be an array type (e.g., `User[]`).
674
+ * For heterogeneous batches, `Out` should be a tuple type (e.g., `[User, Product, Order]`).
675
+ *
676
+ * @returns A promise that resolves to an array or tuple of all results (typed as Out)
677
+ * @throws {Error} If an error occurs and no error handler is registered
678
+ */
679
+ this.execute = async () => {
680
+ try {
681
+ const results = await this.executeAllRequestsHeterogeneous(
682
+ this.requestList
683
+ );
684
+ if (this.resultHandler && results.length > 0) {
685
+ this.resultHandler(results);
686
+ }
687
+ return results;
688
+ } catch (error) {
689
+ if (this.errorHandler) {
690
+ this.errorHandler(error);
691
+ return Promise.reject(error);
692
+ } else {
693
+ throw error;
694
+ }
695
+ } finally {
696
+ if (this.finishHandler) {
697
+ this.finishHandler();
698
+ }
699
+ }
700
+ };
701
+ /**
702
+ * Executes all request entities in parallel (or with concurrency limit if set), handling preconditions and mappers.
703
+ * Stages with failed preconditions are skipped.
704
+ * This method preserves individual types for each stage, enabling heterogeneous batches.
705
+ *
706
+ * @param requestEntityList - List of pipeline stages to execute
707
+ * @returns A promise that resolves to an array of all stage results (preserves tuple types)
708
+ */
709
+ this.executeAllRequestsHeterogeneous = async (requestEntityList) => {
710
+ const stagesToExecute = requestEntityList.filter(
711
+ (stage) => !stage.precondition || stage.precondition()
712
+ );
713
+ const stageIndices = stagesToExecute.map((_, index) => {
714
+ const originalIndex = requestEntityList.indexOf(stagesToExecute[index]);
715
+ return originalIndex >= 0 ? originalIndex : index;
716
+ });
717
+ const promiseFactories = stagesToExecute.map(
718
+ (requestEntity, localIndex) => async () => {
719
+ try {
720
+ const result = await this.executeSingleHeterogeneous(
721
+ requestEntity,
722
+ void 0
723
+ );
724
+ let mappedResult = result;
725
+ if (requestEntity.mapper) {
726
+ let mapperResult;
727
+ if (isPipelineRequestStage2(requestEntity)) {
728
+ mapperResult = requestEntity.mapper(
729
+ result,
730
+ void 0
731
+ );
732
+ } else if (isPipelineManagerStage2(requestEntity)) {
733
+ mapperResult = requestEntity.mapper(
734
+ result,
735
+ void 0
736
+ );
737
+ } else {
738
+ mapperResult = result;
739
+ }
740
+ mappedResult = mapperResult instanceof Promise ? await mapperResult : mapperResult;
741
+ }
742
+ if (requestEntity.resultInterceptor) {
743
+ await requestEntity.resultInterceptor(mappedResult);
744
+ }
745
+ requestEntity.result = mappedResult;
746
+ return { index: stageIndices[localIndex], result: mappedResult };
747
+ } catch (error) {
748
+ const requestConfig = isPipelineRequestStage2(requestEntity) ? requestEntity.config : void 0;
749
+ error.cause = { ...error.cause, requestConfig };
750
+ if (requestEntity.errorHandler) {
751
+ await requestEntity.errorHandler(error);
752
+ }
753
+ throw error;
754
+ }
755
+ }
756
+ );
757
+ let results;
758
+ if (this.concurrency !== void 0 && this.concurrency > 0) {
759
+ results = await this.executeWithConcurrencyLimitHeterogeneous(
760
+ promiseFactories,
761
+ this.concurrency
762
+ );
763
+ } else {
764
+ const promises = promiseFactories.map((factory) => factory());
765
+ results = await Promise.all(promises);
766
+ }
767
+ results.sort((a, b) => a.index - b.index);
768
+ return results.map((r) => r.result);
769
+ };
770
+ /**
771
+ * Executes promise factories with a concurrency limit.
772
+ * Processes promises in batches, ensuring only a limited number run concurrently.
773
+ * This version preserves index information for tuple type support.
774
+ *
775
+ * @param promiseFactories - Array of functions that return promises when called
776
+ * @param limit - Maximum number of concurrent promises to execute
777
+ * @returns A promise that resolves to an array of results with index information
778
+ */
779
+ this.executeWithConcurrencyLimitHeterogeneous = async (promiseFactories, limit) => {
780
+ const results = new Array(
781
+ promiseFactories.length
782
+ );
783
+ let currentIndex = 0;
784
+ let completedCount = 0;
785
+ let activeCount = 0;
786
+ return new Promise(
787
+ (resolve, reject) => {
788
+ const startNext = () => {
789
+ if (activeCount >= limit || currentIndex >= promiseFactories.length) {
790
+ return;
791
+ }
792
+ const localIndex = currentIndex++;
793
+ const factory = promiseFactories[localIndex];
794
+ activeCount++;
795
+ factory().then((result) => {
796
+ results[localIndex] = result;
797
+ completedCount++;
798
+ activeCount--;
799
+ if (completedCount === promiseFactories.length) {
800
+ resolve(results);
801
+ } else {
802
+ startNext();
803
+ }
804
+ }).catch((error) => {
805
+ activeCount--;
806
+ reject(error);
807
+ });
808
+ };
809
+ for (let i = 0; i < Math.min(limit, promiseFactories.length); i++) {
810
+ startNext();
811
+ }
812
+ }
813
+ );
814
+ };
815
+ /**
816
+ * Executes a single request entity (stage).
817
+ * Handles both request stages and nested manager stages.
818
+ * This version supports heterogeneous types by inferring the type from the stage.
819
+ *
820
+ * @param requestEntity - The pipeline stage to execute
821
+ * @param previousResult - The result from the previous stage (optional, not used in batch)
822
+ * @returns A promise that resolves to the stage result (type inferred from stage)
823
+ * @throws {Error} If the stage type is unknown or all retries are exhausted
824
+ */
825
+ this.executeSingleHeterogeneous = async (requestEntity, previousResult) => {
826
+ if (isPipelineRequestStage2(requestEntity)) {
827
+ const { config, retry, chunkProcessing } = requestEntity;
828
+ const requestConfig = typeof config === "function" ? config(previousResult) : config;
829
+ if (retry) {
830
+ return this.executeWithRetry(
831
+ requestConfig,
832
+ retry,
833
+ chunkProcessing
834
+ );
835
+ }
836
+ const rawResult = await this.adapter.executeRequest(requestConfig);
837
+ return this.processResultWithChunks(rawResult, chunkProcessing);
838
+ } else if (isPipelineManagerStage2(requestEntity)) {
839
+ const { request } = requestEntity;
840
+ const rawResult = await request.execute();
841
+ return rawResult;
842
+ } else {
843
+ throw new Error("Unknown type");
844
+ }
845
+ };
846
+ /**
847
+ * Executes a request with retry logic based on the provided retry configuration.
848
+ * Supports chunk processing for streaming responses.
849
+ *
850
+ * @template Out - The output type
851
+ * @param requestConfig - The request configuration
852
+ * @param retryConfig - The retry configuration
853
+ * @param chunkProcessing - Optional chunk processing configuration
854
+ * @returns A promise that resolves to the request result
855
+ * @throws {Error} If all retry attempts are exhausted
856
+ */
857
+ this.executeWithRetry = async (requestConfig, retryConfig, chunkProcessing) => {
858
+ const maxRetries = retryConfig.maxRetries ?? 3;
859
+ const retryCondition = retryConfig.retryCondition ?? defaultRetryCondition;
860
+ let lastError;
861
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
862
+ try {
863
+ const rawResult = await this.adapter.executeRequest(requestConfig);
864
+ return this.processResultWithChunks(rawResult, chunkProcessing);
865
+ } catch (error) {
866
+ lastError = error instanceof Error ? error : new Error(String(error));
867
+ const shouldRetry = attempt < maxRetries && retryCondition(lastError, attempt);
868
+ if (!shouldRetry) {
869
+ throw lastError;
870
+ }
871
+ const delay = this.calculateRetryDelay(
872
+ attempt + 1,
873
+ lastError,
874
+ retryConfig
875
+ );
876
+ if (delay > 0) {
877
+ await this.sleep(delay);
878
+ }
879
+ }
880
+ }
881
+ throw lastError || new Error("Retry failed");
882
+ };
883
+ /**
884
+ * Processes a result with chunk processing if enabled.
885
+ * Handles streaming responses by processing chunks progressively.
886
+ *
887
+ * @template Out - The output type
888
+ * @param rawResult - The raw result from the adapter
889
+ * @param chunkProcessing - Optional chunk processing configuration
890
+ * @returns A promise that resolves to the processed result
891
+ */
892
+ this.processResultWithChunks = async (rawResult, chunkProcessing) => {
893
+ if (chunkProcessing?.enabled && rawResult instanceof Response && hasReadableStream(rawResult)) {
894
+ const clonedResponse = rawResult.clone();
895
+ const processed = await processResponseStream(clonedResponse, {
896
+ ...chunkProcessing
897
+ });
898
+ if (chunkProcessing.accumulate && processed !== rawResult) {
899
+ return processed;
900
+ }
901
+ return this.adapter.getResult(rawResult);
902
+ }
903
+ return this.adapter.getResult(rawResult);
904
+ };
905
+ }
906
+ /**
907
+ * Sets the maximum number of concurrent requests to execute simultaneously.
908
+ * If not set, all requests will execute in parallel.
909
+ *
910
+ * @param limit - Maximum number of concurrent requests (must be > 0)
911
+ * @returns The current RequestBatch instance for method chaining
912
+ * @throws {Error} If limit is less than or equal to 0
913
+ *
914
+ * @example
915
+ * ```typescript
916
+ * const batch = new RequestBatch<User[], Response, FetchRequestConfig>();
917
+ * batch.setRequestAdapter(adapter);
918
+ * batch.withConcurrency(5); // Execute max 5 requests at a time
919
+ * batch.addAll([...requests]);
920
+ * const results = await batch.execute();
921
+ * ```
922
+ */
923
+ withConcurrency(limit) {
924
+ if (limit <= 0) {
925
+ throw new Error("Concurrency limit must be greater than 0");
926
+ }
927
+ this.concurrency = limit;
928
+ return this;
929
+ }
930
+ /**
931
+ * Calculates the delay before the next retry attempt.
932
+ *
933
+ * @param attempt - The current attempt number (1-indexed for retries)
934
+ * @param error - The error that occurred
935
+ * @param retryConfig - The retry configuration
936
+ * @returns The delay in milliseconds
937
+ */
938
+ calculateRetryDelay(attempt, error, retryConfig) {
939
+ const baseDelay = retryConfig.retryDelay ?? 1e3;
940
+ const maxDelay = retryConfig.maxDelay;
941
+ let delay;
942
+ if (typeof baseDelay === "function") {
943
+ delay = baseDelay(attempt, error);
944
+ } else if (retryConfig.exponentialBackoff) {
945
+ delay = baseDelay * Math.pow(2, attempt - 1);
946
+ if (maxDelay !== void 0 && delay > maxDelay) {
947
+ delay = maxDelay;
948
+ }
949
+ } else {
950
+ delay = baseDelay;
951
+ }
952
+ return Math.max(0, delay);
953
+ }
954
+ /**
955
+ * Sleeps for the specified number of milliseconds.
956
+ *
957
+ * @param ms - Milliseconds to sleep
958
+ * @returns A promise that resolves after the delay
959
+ */
960
+ sleep(ms) {
961
+ return new Promise((resolve) => setTimeout(resolve, ms));
962
+ }
963
+ };
964
+ /**
965
+ * Creates a new RequestBatch with stages and adapter.
966
+ * This is the entry point for building a request batch.
967
+ *
968
+ * Supports both homogeneous batches (all requests return the same type) and
969
+ * heterogeneous batches (each request can return a different type, using tuple types).
970
+ *
971
+ * @template Out - The output type (should be an array type, e.g., `User[]` for homogeneous batches)
972
+ * @template AdapterExecutionResult - The type of result returned by the adapter
973
+ * @template RequestConfig - The type of request configuration
974
+ * @param stages - Array of pipeline stages (request or manager stages)
975
+ * @param adapter - The request adapter to use for HTTP requests
976
+ * @returns A new RequestBatch instance with the stages and adapter configured
977
+ *
978
+ * @example
979
+ * ```typescript
980
+ * // Homogeneous batch - all requests return User
981
+ * const batch = RequestBatch.batch(
982
+ * [
983
+ * { config: { url: '/api/users/1', method: 'GET' } },
984
+ * { config: { url: '/api/users/2', method: 'GET' } },
985
+ * { config: { url: '/api/users/3', method: 'GET' } }
986
+ * ],
987
+ * adapter
988
+ * );
989
+ * const results: User[] = await batch.execute();
990
+ *
991
+ * // Heterogeneous batch - each request returns a different type
992
+ * const heterogeneousBatch = RequestBatch.batch(
993
+ * [
994
+ * { config: { url: '/api/users/1', method: 'GET' }, mapper: (r) => r.json() as Promise<User> },
995
+ * { config: { url: '/api/products/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Product> },
996
+ * { config: { url: '/api/orders/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Order> }
997
+ * ],
998
+ * adapter
999
+ * );
1000
+ * const results: [User, Product, Order] = await heterogeneousBatch.execute();
1001
+ * ```
1002
+ */
1003
+ _RequestBatch.batch = (stages, adapter) => {
1004
+ const batch2 = new _RequestBatch();
1005
+ batch2.setRequestAdapter(adapter);
1006
+ batch2.addAll([...stages]);
1007
+ return batch2;
1008
+ };
1009
+ var RequestBatch = _RequestBatch;
1010
+ function isPipelineRequestStage2(stage) {
1011
+ return "config" in stage && !("request" in stage);
1012
+ }
1013
+ function isPipelineManagerStage2(stage) {
1014
+ return "request" in stage && !("config" in stage);
1015
+ }
1016
+ function batch(stages, adapter) {
1017
+ return RequestBatch.batch(stages, adapter);
1018
+ }
1019
+
1020
+ export { RequestAdapter, RequestBatch, RequestChain, RequestFlow as RequestManager, SSRFError, batch, begin, RequestChain as default, defaultRetryCondition, getErrorStatus, hasReadableStream, isNetworkError, isReadableStream, processResponseStream, processStream, processTextStreamLineByLine, retryOnNetworkOrStatusCodes, retryOnStatusCodes, validateUrl };
666
1021
  //# sourceMappingURL=index.js.map
667
1022
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/url-validator.ts","../src/request-adapter.ts","../src/request-manager.ts","../src/utils/retry-utils.ts","../src/utils/chunk-processor.ts","../src/request-chain.ts"],"names":["previousEntity","previousResult"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * URL validation utilities to prevent SSRF (Server-Side Request Forgery) attacks\n */\n\nexport interface UrlValidationOptions {\n /**\n * Allow private/internal IP addresses (default: false)\n * WARNING: Enabling this can expose your application to SSRF attacks\n */\n allowPrivateIPs?: boolean;\n\n /**\n * Allow localhost addresses (default: false)\n * WARNING: Enabling this can expose your application to SSRF attacks\n */\n allowLocalhost?: boolean;\n\n /**\n * Custom list of allowed protocols (default: ['http:', 'https:'])\n */\n allowedProtocols?: string[];\n\n /**\n * Disable URL validation entirely (default: false)\n * WARNING: This completely disables SSRF protection\n */\n disableValidation?: boolean;\n}\n\nexport class SSRFError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SSRFError\";\n }\n}\n\n/**\n * Private IP ranges that should be blocked to prevent SSRF attacks\n */\nconst PRIVATE_IP_RANGES = [\n // IPv4 private ranges\n /^10\\./,\n /^172\\.(1[6-9]|2[0-9]|3[01])\\./,\n /^192\\.168\\./,\n // IPv6 private ranges\n /^fc00:/i,\n /^fe80:/i,\n /^::1$/,\n /^fd/,\n];\n\n/**\n * Validates a URL to prevent SSRF attacks\n * @param url - The URL to validate\n * @param options - Validation options\n * @throws {SSRFError} If the URL is invalid or potentially dangerous\n */\nexport function validateUrl(\n url: string,\n options: UrlValidationOptions = {}\n): void {\n const {\n allowPrivateIPs = false,\n allowLocalhost = false,\n allowedProtocols = [\"http:\", \"https:\"],\n disableValidation = false,\n } = options;\n\n if (disableValidation) {\n return;\n }\n\n if (!url || typeof url !== \"string\") {\n throw new SSRFError(\"URL must be a non-empty string\");\n }\n\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(url);\n } catch {\n throw new SSRFError(`Invalid URL format: ${url}`);\n }\n\n // Validate protocol\n const protocol = parsedUrl.protocol.toLowerCase();\n if (!allowedProtocols.includes(protocol)) {\n throw new SSRFError(\n `Protocol \"${protocol}\" is not allowed. Only ${allowedProtocols.join(\", \")} are permitted.`\n );\n }\n\n // Check for localhost\n const hostname = parsedUrl.hostname.toLowerCase();\n // Normalize IPv6 addresses (remove brackets if present)\n const normalizedHostname = hostname.replace(/^\\[|\\]$/g, \"\");\n const isLocalhost =\n normalizedHostname === \"localhost\" ||\n normalizedHostname === \"127.0.0.1\" ||\n normalizedHostname === \"::1\" ||\n normalizedHostname.startsWith(\"127.\") ||\n normalizedHostname === \"0.0.0.0\";\n\n if (isLocalhost && !allowLocalhost) {\n throw new SSRFError(\n \"Localhost addresses are not allowed for security reasons. Set allowLocalhost=true to override.\"\n );\n }\n\n // Check for private IP ranges\n if (!allowPrivateIPs) {\n // Check IPv4 addresses first with specific error messages\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(normalizedHostname)) {\n const parts = normalizedHostname.split(\".\").map(Number);\n const [a, b] = parts;\n\n // 10.0.0.0/8\n if (a === 10) {\n throw new SSRFError(\n \"Private IP addresses (10.x.x.x) are not allowed for security reasons.\"\n );\n }\n\n // 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) {\n throw new SSRFError(\n \"Private IP addresses (172.16-31.x.x) are not allowed for security reasons.\"\n );\n }\n\n // 192.168.0.0/16\n if (a === 192 && b === 168) {\n throw new SSRFError(\n \"Private IP addresses (192.168.x.x) are not allowed for security reasons.\"\n );\n }\n\n // 169.254.0.0/16 (link-local)\n if (a === 169 && b === 254) {\n throw new SSRFError(\n \"Link-local addresses (169.254.x.x) are not allowed for security reasons.\"\n );\n }\n }\n\n // Check IPv6 and other private IP ranges using regex\n const isPrivateIP = PRIVATE_IP_RANGES.some((range) =>\n range.test(normalizedHostname)\n );\n\n if (isPrivateIP) {\n throw new SSRFError(\n \"Private/internal IP addresses are not allowed for security reasons. Set allowPrivateIPs=true to override.\"\n );\n }\n }\n}\n","import type { IRequestConfig } from \"./models/request-params\";\nimport { validateUrl, type UrlValidationOptions } from \"./utils/url-validator\";\n\n/**\n * Abstract base class for request adapters that handle HTTP requests.\n * Provides URL validation and a common interface for different HTTP client implementations.\n *\n * @template ExecutionResult - The type of result returned by the adapter's HTTP client\n * @template RequestConfig - The type of request configuration, must extend IRequestConfig\n *\n * @example\n * ```typescript\n * class MyAdapter extends RequestAdapter<Response> {\n * async createRequest(config: IRequestConfig): Promise<Response> {\n * // Implementation\n * }\n * }\n * ```\n */\nexport default abstract class RequestAdapter<\n ExecutionResult,\n RequestConfig extends IRequestConfig = IRequestConfig,\n> {\n /**\n * URL validation options used to prevent SSRF attacks\n */\n protected urlValidationOptions: UrlValidationOptions;\n\n /**\n * Creates a new RequestAdapter instance.\n *\n * @param urlValidationOptions - Options for URL validation to prevent SSRF attacks\n */\n constructor(urlValidationOptions: UrlValidationOptions = {}) {\n this.urlValidationOptions = urlValidationOptions;\n }\n\n /**\n * Creates and executes an HTTP request using the adapter's underlying HTTP client.\n * This method must be implemented by concrete adapter classes.\n *\n * @param requestConfig - The request configuration object\n * @returns A promise that resolves to the execution result\n */\n public abstract createRequest(\n requestConfig: RequestConfig\n ): Promise<ExecutionResult>;\n\n /**\n * Type-safe getter for the execution result.\n * Allows casting the result to a specific type.\n *\n * @template T - The desired result type\n * @param result - The execution result to cast\n * @returns The result cast to type T\n */\n public getResult<T extends ExecutionResult>(result: ExecutionResult): T {\n return result as T;\n }\n\n /**\n * Executes a request with URL validation.\n * Validates the URL to prevent SSRF attacks before creating the request.\n *\n * @param requestConfig - The request configuration object\n * @returns A promise that resolves to the execution result\n * @throws {SSRFError} If the URL is invalid or potentially dangerous\n */\n public executeRequest(\n requestConfig: RequestConfig\n ): Promise<ExecutionResult> {\n // Validate URL to prevent SSRF attacks\n validateUrl(requestConfig.url, this.urlValidationOptions);\n return this.createRequest(requestConfig);\n }\n}\n","import type RequestAdapter from \"./request-adapter\";\nimport type {\n PipelineManagerStage,\n PipelineRequestStage,\n} from \"./models/request-params\";\nimport type { ErrorHandler, ResultHandler } from \"./models/handlers\";\nimport type { IRequestConfig } from \"./models/request-params\";\n\n/**\n * Abstract base class for managing request pipelines and flows.\n * Provides functionality for chaining requests, error handling, and result processing.\n *\n * @template Out - The output type of the request flow\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template RequestConfig - The type of request configuration, must extend IRequestConfig\n */\nexport default abstract class RequestFlow<\n Out,\n AdapterExecutionResult = Out,\n RequestConfig extends IRequestConfig = IRequestConfig,\n> {\n /**\n * List of pipeline stages to execute\n */\n protected requestList: (\n | PipelineRequestStage<any, any, any>\n | PipelineManagerStage<any, any, any>\n )[] = [];\n /**\n * Optional error handler callback\n */\n protected errorHandler?: ErrorHandler;\n /**\n * Optional result handler callback\n */\n protected resultHandler?: ResultHandler<Out | Out[]>;\n /**\n * Optional finish handler callback executed after completion\n */\n protected finishHandler?: () => void;\n /**\n * The request adapter used to execute HTTP requests\n */\n protected adapter: RequestAdapter<AdapterExecutionResult, RequestConfig>;\n\n /**\n * Executes the request flow and returns the final result.\n * Must be implemented by concrete subclasses.\n *\n * @returns A promise that resolves to the output result\n */\n public abstract execute(): Promise<Out>;\n\n /**\n * Sets the request adapter to use for executing HTTP requests.\n *\n * @param adapter - The request adapter instance\n * @returns The current RequestFlow instance for method chaining\n */\n public setRequestAdapter(\n adapter: RequestAdapter<AdapterExecutionResult, RequestConfig>\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.adapter = adapter;\n return this;\n }\n\n /**\n * Adds multiple pipeline stages to the request list.\n *\n * @param requestList - Array of pipeline stages to add\n * @returns The current RequestFlow instance for method chaining\n */\n public addAll(\n requestList: Array<\n | PipelineRequestStage<AdapterExecutionResult, Out, RequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, RequestConfig>\n > = []\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.requestList = this.requestList.concat(requestList);\n return this;\n }\n\n /**\n * Sets an error handler callback that will be called when an error occurs during execution.\n *\n * @param errorHandler - Function to handle errors\n * @returns The current RequestFlow instance for method chaining\n */\n public withErrorHandler(\n errorHandler: ErrorHandler\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.errorHandler = errorHandler;\n return this;\n }\n\n /**\n * Sets a result handler callback that will be called with the execution result.\n *\n * @param resultHandler - Function to handle results\n * @returns The current RequestFlow instance for method chaining\n */\n public withResultHandler(\n resultHandler: ResultHandler<Out | Out[]>\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.resultHandler = resultHandler;\n return this;\n }\n\n /**\n * Sets a finish handler callback that will be called after execution completes (success or failure).\n *\n * @param finishHandler - Function to execute on completion\n * @returns The current RequestFlow instance for method chaining\n */\n public withFinishHandler(\n finishHandler: () => void\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.finishHandler = finishHandler;\n return this;\n }\n}\n","/**\n * Utility functions for retry logic and error handling.\n */\n\n/**\n * Attempts to extract HTTP status code from an error object.\n * Works with different adapter error formats (Axios, Fetch, etc.).\n *\n * @param error - The error object\n * @returns The HTTP status code if available, undefined otherwise\n */\nexport function getErrorStatus(error: Error): number | undefined {\n // Axios errors have response.status\n if (\n typeof (error as any).response !== \"undefined\" &&\n typeof (error as any).response.status === \"number\"\n ) {\n return (error as any).response.status;\n }\n\n // Some adapters might put status directly on error\n if (typeof (error as any).status === \"number\") {\n return (error as any).status;\n }\n\n // Check for statusCode (some libraries use this)\n if (typeof (error as any).statusCode === \"number\") {\n return (error as any).statusCode;\n }\n\n return undefined;\n}\n\n/**\n * Checks if an error is a network error (connection failure, timeout, etc.).\n *\n * @param error - The error object\n * @returns True if the error appears to be a network error\n */\nexport function isNetworkError(error: Error): boolean {\n // Common network error names\n const networkErrorNames = [\n \"TypeError\",\n \"NetworkError\",\n \"TimeoutError\",\n \"AbortError\",\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n ];\n\n if (networkErrorNames.includes(error.name)) {\n return true;\n }\n\n // Check error message for network-related keywords\n const networkKeywords = [\n \"network\",\n \"connection\",\n \"timeout\",\n \"fetch\",\n \"failed to fetch\",\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n ];\n\n const errorMessage = error.message?.toLowerCase() || \"\";\n return networkKeywords.some((keyword) => errorMessage.includes(keyword));\n}\n\n/**\n * Default retry condition that retries on network errors.\n * This is used when no retryCondition is provided in RetryConfig.\n *\n * @param error - The error that occurred\n * @returns True if the error is a network error\n */\nexport function defaultRetryCondition(error: Error): boolean {\n return isNetworkError(error);\n}\n\n/**\n * Helper function to create a retry condition that retries on specific HTTP status codes.\n *\n * @param statusCodes - Array of HTTP status codes to retry on\n * @returns A retry condition function\n *\n * @example\n * ```typescript\n * retry: {\n * retryCondition: retryOnStatusCodes(500, 502, 503, 504, 429)\n * }\n * ```\n */\nexport function retryOnStatusCodes(\n ...statusCodes: number[]\n): (error: Error) => boolean {\n return (error: Error) => {\n const status = getErrorStatus(error);\n return status !== undefined && statusCodes.includes(status);\n };\n}\n\n/**\n * Helper function to create a retry condition that retries on network errors OR specific status codes.\n *\n * @param statusCodes - Array of HTTP status codes to retry on\n * @returns A retry condition function\n *\n * @example\n * ```typescript\n * retry: {\n * retryCondition: retryOnNetworkOrStatusCodes(500, 502, 503, 504, 429)\n * }\n * ```\n */\nexport function retryOnNetworkOrStatusCodes(\n ...statusCodes: number[]\n): (error: Error, attempt: number) => boolean {\n return (error: Error) => {\n // Always retry network errors\n if (isNetworkError(error)) {\n return true;\n }\n // Retry on specified status codes\n const status = getErrorStatus(error);\n return status !== undefined && statusCodes.includes(status);\n };\n}\n","import type { ChunkHandler } from \"../models/handlers\";\nimport type { ChunkProcessingConfig } from \"../models/request-params\";\n\n/**\n * Processes a ReadableStream progressively, calling the chunk handler for each chunk.\n * Supports both text and binary streams.\n *\n * @template Chunk - The type of chunk data\n * @param stream - The ReadableStream to process\n * @param config - The chunk processing configuration\n * @returns A promise that resolves when processing is complete, optionally with accumulated data\n */\nexport async function processStream<Chunk = string | Uint8Array>(\n stream: ReadableStream<Uint8Array>,\n config: ChunkProcessingConfig<Chunk>\n): Promise<Chunk | undefined> {\n const { chunkHandler, encoding = \"utf-8\", accumulate = false } = config;\n\n const reader = stream.getReader();\n const decoder = new TextDecoder(encoding);\n let accumulatedData: string | Uint8Array | undefined;\n let chunkIndex = 0;\n let totalBytesRead = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n // Process any remaining accumulated data\n if (accumulatedData !== undefined && accumulatedData.length > 0) {\n await processChunk(\n accumulatedData as Chunk,\n chunkHandler,\n chunkIndex,\n true,\n totalBytesRead\n );\n }\n break;\n }\n\n totalBytesRead += value.length;\n\n if (accumulate) {\n // Accumulate chunks\n if (accumulatedData === undefined) {\n accumulatedData = value;\n } else if (typeof accumulatedData === \"string\") {\n accumulatedData += decoder.decode(value, { stream: true });\n } else {\n // Concatenate Uint8Arrays\n const combined = new Uint8Array(\n accumulatedData.length + value.length\n );\n combined.set(accumulatedData);\n combined.set(value, accumulatedData.length);\n accumulatedData = combined;\n }\n }\n\n // Process chunk\n const isLast = false; // We don't know if it's the last until done\n await processChunk(\n value as Chunk,\n chunkHandler,\n chunkIndex,\n isLast,\n totalBytesRead\n );\n\n chunkIndex++;\n }\n\n return accumulatedData as Chunk | undefined;\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Processes a text stream line by line (useful for NDJSON, CSV, or line-delimited data).\n *\n * @param stream - The ReadableStream to process\n * @param config - The chunk processing configuration\n * @returns A promise that resolves when processing is complete\n */\nexport async function processTextStreamLineByLine(\n stream: ReadableStream<Uint8Array>,\n config: ChunkProcessingConfig<string>\n): Promise<string | undefined> {\n const { chunkHandler, encoding = \"utf-8\", accumulate = false } = config;\n\n const reader = stream.getReader();\n const decoder = new TextDecoder(encoding);\n let buffer = \"\";\n let lineIndex = 0;\n let totalBytesRead = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n // Process any remaining buffer content\n if (buffer.length > 0) {\n await processChunk(\n buffer,\n chunkHandler,\n lineIndex,\n true,\n totalBytesRead\n );\n }\n break;\n }\n\n totalBytesRead += value.length;\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.length > 0) {\n // Only process non-empty lines\n await processChunk(\n line,\n chunkHandler,\n lineIndex,\n false,\n totalBytesRead\n );\n lineIndex++;\n }\n }\n }\n\n return accumulate ? buffer : undefined;\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Processes a Response body as a stream with chunk processing.\n * Automatically detects if the response has a readable stream and processes it.\n *\n * @template Chunk - The type of chunk data\n * @param response - The Response object\n * @param config - The chunk processing configuration\n * @returns A promise that resolves with the processed result or the original response\n */\nexport async function processResponseStream<Chunk = string | Uint8Array>(\n response: Response,\n config: ChunkProcessingConfig<Chunk>\n): Promise<Response | Chunk> {\n if (!response.body) {\n throw new Error(\"Response body is not available for streaming\");\n }\n\n const processed = await processStream(response.body, config);\n\n // If accumulation is enabled, return the accumulated data\n // Otherwise, return the original response (chunks were processed via handler)\n if (config.accumulate && processed !== undefined) {\n return processed;\n }\n\n // Return a new Response with the processed data if needed\n // For now, return original response since chunks were handled via handler\n return response;\n}\n\n/**\n * Helper function to process a single chunk with the handler.\n *\n * @template Chunk - The type of chunk data\n * @param chunk - The chunk to process\n * @param handler - The chunk handler function\n * @param index - The chunk index\n * @param isLast - Whether this is the last chunk\n * @param totalBytesRead - Total bytes read so far\n */\nasync function processChunk<Chunk>(\n chunk: Chunk,\n handler: ChunkHandler<Chunk>,\n index: number,\n isLast: boolean,\n totalBytesRead: number\n): Promise<void> {\n const result = handler(chunk, {\n index,\n isLast,\n totalBytesRead,\n });\n\n if (result instanceof Promise) {\n await result;\n }\n}\n\n/**\n * Checks if a value is a ReadableStream.\n *\n * @param value - The value to check\n * @returns True if the value is a ReadableStream\n */\nexport function isReadableStream(\n value: unknown\n): value is ReadableStream<Uint8Array> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"getReader\" in value &&\n typeof (value as ReadableStream).getReader === \"function\"\n );\n}\n\n/**\n * Checks if a Response has a readable body stream.\n *\n * @param response - The Response to check\n * @returns True if the response has a readable body stream\n */\nexport function hasReadableStream(response: Response): boolean {\n return response.body !== null && isReadableStream(response.body);\n}\n","import type RequestAdapter from \"./request-adapter\";\nimport RequestFlow from \"./request-manager\";\nimport type {\n IRequestConfig,\n PipelineRequestStage,\n PipelineManagerStage,\n RetryConfig,\n} from \"./models/request-params\";\nimport { defaultRetryCondition } from \"./utils/retry-utils\";\nimport {\n processResponseStream,\n hasReadableStream,\n} from \"./utils/chunk-processor\";\n\n/**\n * A chainable request pipeline that allows sequential execution of HTTP requests.\n * Each stage can depend on the result of the previous stage, and stages can be conditionally executed.\n *\n * @template Out - The output type of the current chain\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template AdapterRequestConfig - The type of request configuration\n * @template Types - Tuple type tracking all output types in the chain\n *\n * @example\n * ```typescript\n * const chain = RequestChain.begin(\n * { config: { url: 'https://api.example.com/users', method: 'GET' } },\n * adapter\n * ).next({ config: { url: 'https://api.example.com/posts', method: 'GET' } });\n *\n * const result = await chain.execute();\n * ```\n */\nexport default class RequestChain<\n Out,\n AdapterExecutionResult = Out,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n Types extends readonly unknown[] = [Out],\n> extends RequestFlow<Out, AdapterExecutionResult, AdapterRequestConfig> {\n // #region Public methods\n\n /**\n * Creates a new RequestChain with an initial stage.\n * This is the entry point for building a request chain.\n *\n * @template Out - The output type of the initial stage\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template AdapterRequestConfig - The type of request configuration\n * @param stage - The initial pipeline stage (request or manager stage)\n * @param adapter - The request adapter to use for HTTP requests\n * @returns A new RequestChain instance with the initial stage\n */\n public static begin = <\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n >(\n stage:\n | PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>,\n adapter: RequestAdapter<AdapterExecutionResult, AdapterRequestConfig>\n ): RequestChain<Out, AdapterExecutionResult, AdapterRequestConfig, [Out]> => {\n const requestChain = new RequestChain<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n []\n >();\n requestChain.setRequestAdapter(adapter);\n return requestChain.next(stage);\n };\n\n /**\n * Adds a new stage to the request chain and returns a new chain with updated types.\n * This method enables type-safe chaining of requests.\n *\n * @template NewOut - The output type of the new stage\n * @param stage - The pipeline stage to add (request or manager stage)\n * @returns A new RequestChain instance with the added stage\n */\n public next = <NewOut>(\n stage:\n | PipelineRequestStage<\n AdapterExecutionResult,\n NewOut,\n AdapterRequestConfig,\n Out\n >\n | PipelineManagerStage<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n Out\n >\n ): RequestChain<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n [...Types, NewOut]\n > => {\n return this.addRequestEntity(stage);\n };\n\n /**\n * Executes all stages in the chain sequentially and returns the final result.\n * Handles errors and calls registered handlers appropriately.\n *\n * @returns A promise that resolves to the final output result\n * @throws {Error} If an error occurs and no error handler is registered\n */\n public execute = async (): Promise<Out> => {\n try {\n const results: Out[] = await this.executeAllRequests(this.requestList);\n const result: Out = results[results.length - 1];\n if (this.resultHandler && result) {\n this.resultHandler(result);\n }\n return result;\n } catch (error) {\n if (this.errorHandler) {\n this.errorHandler(error);\n return Promise.reject(error);\n } else {\n throw error;\n }\n } finally {\n if (this.finishHandler) {\n this.finishHandler();\n }\n }\n };\n\n /**\n * Executes all stages in the chain and returns all results as a tuple.\n * Useful when you need access to intermediate results.\n *\n * @returns A promise that resolves to a tuple of all stage results\n * @throws {Error} If an error occurs and no error handler is registered\n */\n public async executeAll(): Promise<Types> {\n try {\n const results = await this.executeAllRequests(this.requestList);\n if (this.resultHandler && results.length > 0) {\n this.resultHandler(results);\n }\n return results as unknown as Types;\n } catch (error) {\n if (this.errorHandler) {\n this.errorHandler(error);\n return Promise.reject(error);\n } else {\n throw error;\n }\n } finally {\n if (this.finishHandler) {\n this.finishHandler();\n }\n }\n }\n\n // #endregion\n\n // #region Private methods\n\n /**\n * Adds a request entity (stage) to the internal request list.\n *\n * @template NewOut - The output type of the new stage\n * @param stage - The pipeline stage to add\n * @returns A new RequestChain instance with updated types\n */\n private addRequestEntity = <NewOut, PrevOut = Out>(\n stage:\n | PipelineRequestStage<\n AdapterExecutionResult,\n NewOut,\n AdapterRequestConfig,\n PrevOut\n >\n | PipelineManagerStage<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n PrevOut\n >\n ): RequestChain<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n [...Types, NewOut]\n > => {\n this.requestList.push(stage);\n return this as unknown as RequestChain<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n [...Types, NewOut]\n >;\n };\n\n /**\n * Executes all request entities in sequence, handling preconditions and mappers.\n * Stages with failed preconditions are skipped but preserve the previous result.\n *\n * @template Out - The output type\n * @param requestEntityList - List of pipeline stages to execute\n * @returns A promise that resolves to an array of all stage results\n */\n private executeAllRequests = async <Out>(\n requestEntityList: (\n | PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>\n )[]\n ): Promise<Out[]> => {\n const results: Out[] = [];\n for (let i = 0; i < requestEntityList.length; i++) {\n const requestEntity:\n | PipelineRequestStage<\n AdapterExecutionResult,\n Out,\n AdapterRequestConfig\n >\n | PipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig\n > = requestEntityList[i];\n\n // Check precondition - skip stage if precondition returns false\n if (requestEntity.precondition && !requestEntity.precondition()) {\n const previousEntity = requestEntityList[i - 1];\n const previousResult: Out | undefined = previousEntity?.result;\n // Use previous result or undefined if this is the first stage\n // Don't push to results - skipped stages don't produce new results\n requestEntityList[i].result = previousResult as Out | undefined;\n continue;\n }\n\n const previousEntity = requestEntityList[i - 1];\n const previousResult: Out | undefined = previousEntity?.result;\n try {\n const requestResult: Out = await this.executeSingle<Out>(\n requestEntity,\n previousResult\n );\n let result: Out = requestResult;\n if (requestEntity.mapper) {\n let mappedResult: Out | Promise<Out>;\n if (isPipelineRequestStage(requestEntity)) {\n mappedResult = requestEntity.mapper(\n requestResult as unknown as AdapterExecutionResult,\n previousResult\n );\n } else if (isPipelineManagerStage(requestEntity)) {\n mappedResult = requestEntity.mapper(\n requestResult as unknown as Out,\n previousResult\n );\n } else {\n mappedResult = result;\n }\n result =\n mappedResult instanceof Promise ? await mappedResult : mappedResult;\n }\n if (requestEntity.resultInterceptor) {\n await requestEntity.resultInterceptor(result);\n }\n requestEntityList[i].result = result as Out;\n results.push(result);\n } catch (error) {\n const requestConfig = isPipelineRequestStage(requestEntity)\n ? requestEntity.config\n : undefined;\n error.cause = { ...error.cause, requestConfig };\n if (requestEntity.errorHandler) {\n await requestEntity.errorHandler(error);\n }\n throw error;\n }\n }\n return results;\n };\n\n /**\n * Executes a single request entity (stage).\n * Handles both request stages and nested manager stages.\n * Implements retry logic for request stages when retry configuration is provided.\n * Supports progressive chunk processing for streaming responses.\n *\n * @template Out - The output type\n * @param requestEntity - The pipeline stage to execute\n * @param previousResult - The result from the previous stage (optional)\n * @returns A promise that resolves to the stage result\n * @throws {Error} If the stage type is unknown or all retries are exhausted\n */\n private executeSingle = async <Out>(\n requestEntity:\n | PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>,\n previousResult?: Out\n ): Promise<Out> => {\n if (isPipelineRequestStage(requestEntity)) {\n const { config, retry, chunkProcessing } = requestEntity;\n const requestConfig: AdapterRequestConfig =\n typeof config === \"function\"\n ? (config(previousResult as Out) as AdapterRequestConfig)\n : (config as AdapterRequestConfig);\n\n // If retry config is provided, wrap execution in retry logic\n if (retry) {\n return this.executeWithRetry<Out>(\n requestConfig,\n retry,\n chunkProcessing\n );\n }\n\n // Execute request and handle chunk processing if enabled\n const rawResult: AdapterExecutionResult =\n await this.adapter.executeRequest(requestConfig);\n return this.processResultWithChunks<Out>(rawResult, chunkProcessing);\n } else if (isPipelineManagerStage(requestEntity)) {\n const { request } = requestEntity;\n const rawResult: Out = await request.execute();\n // For nested managers, the result is already processed, so we return it directly\n // The adapter's getResult expects AdapterExecutionResult, but nested results are already Out\n return rawResult;\n } else {\n throw new Error(\"Unknown type\");\n }\n };\n\n /**\n * Executes a request with retry logic based on the provided retry configuration.\n * Supports chunk processing for streaming responses.\n *\n * @template Out - The output type\n * @param requestConfig - The request configuration\n * @param retryConfig - The retry configuration\n * @param chunkProcessing - Optional chunk processing configuration\n * @returns A promise that resolves to the request result\n * @throws {Error} If all retry attempts are exhausted\n */\n private executeWithRetry = async <Out>(\n requestConfig: AdapterRequestConfig,\n retryConfig: RetryConfig,\n chunkProcessing?: import(\"./models/request-params\").ChunkProcessingConfig\n ): Promise<Out> => {\n const maxRetries = retryConfig.maxRetries ?? 3;\n const retryCondition = retryConfig.retryCondition ?? defaultRetryCondition;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const rawResult: AdapterExecutionResult =\n await this.adapter.executeRequest(requestConfig);\n return this.processResultWithChunks<Out>(rawResult, chunkProcessing);\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Check if we should retry\n const shouldRetry =\n attempt < maxRetries && retryCondition(lastError, attempt);\n\n if (!shouldRetry) {\n throw lastError;\n }\n\n // Calculate delay before retrying\n const delay = this.calculateRetryDelay(\n attempt + 1,\n lastError,\n retryConfig\n );\n\n // Wait before retrying\n if (delay > 0) {\n await this.sleep(delay);\n }\n }\n }\n\n // This should never be reached, but TypeScript needs it\n throw lastError || new Error(\"Retry failed\");\n };\n\n /**\n * Processes a result with chunk processing if enabled.\n * Handles streaming responses by processing chunks progressively.\n *\n * @template Out - The output type\n * @param rawResult - The raw result from the adapter\n * @param chunkProcessing - Optional chunk processing configuration\n * @returns A promise that resolves to the processed result\n */\n private processResultWithChunks = async <Out>(\n rawResult: AdapterExecutionResult,\n chunkProcessing?: import(\"./models/request-params\").ChunkProcessingConfig\n ): Promise<Out> => {\n // If chunk processing is enabled and result is a Response with readable stream\n if (\n chunkProcessing?.enabled &&\n rawResult instanceof Response &&\n hasReadableStream(rawResult)\n ) {\n // Clone the response to avoid consuming the original stream\n const clonedResponse = rawResult.clone();\n\n // Process the stream\n const processed = await processResponseStream(clonedResponse, {\n ...chunkProcessing,\n });\n\n // If accumulation is enabled, return the accumulated data\n // Otherwise, return the original response (chunks were processed via handler)\n if (chunkProcessing.accumulate && processed !== rawResult) {\n return processed as unknown as Out;\n }\n\n // Return original response if chunks were only processed via handler\n return this.adapter.getResult(rawResult) as unknown as Out;\n }\n\n // No chunk processing, return result normally\n return this.adapter.getResult(rawResult) as unknown as Out;\n };\n\n /**\n * Calculates the delay before the next retry attempt.\n *\n * @param attempt - The current attempt number (1-indexed for retries)\n * @param error - The error that occurred\n * @param retryConfig - The retry configuration\n * @returns The delay in milliseconds\n */\n private calculateRetryDelay(\n attempt: number,\n error: Error,\n retryConfig: RetryConfig\n ): number {\n const baseDelay = retryConfig.retryDelay ?? 1000;\n const maxDelay = retryConfig.maxDelay;\n\n let delay: number;\n\n if (typeof baseDelay === \"function\") {\n // Custom delay function\n delay = baseDelay(attempt, error);\n } else if (retryConfig.exponentialBackoff) {\n // Exponential backoff: delay * 2^attempt\n delay = baseDelay * Math.pow(2, attempt - 1);\n // Apply maxDelay cap if provided\n if (maxDelay !== undefined && delay > maxDelay) {\n delay = maxDelay;\n }\n } else {\n // Fixed delay\n delay = baseDelay;\n }\n\n return Math.max(0, delay);\n }\n\n /**\n * Sleeps for the specified number of milliseconds.\n *\n * @param ms - Milliseconds to sleep\n * @returns A promise that resolves after the delay\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // #endregion\n}\n\n/**\n * Creates a new RequestChain with an initial stage.\n * This is a convenience function that wraps RequestChain.begin.\n *\n * @template Out - The output type of the initial stage\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template AdapterRequestConfig - The type of request configuration\n * @param stage - The initial pipeline stage (request or manager stage)\n * @param adapter - The request adapter to use for HTTP requests\n * @returns A new RequestChain instance with the initial stage\n */\nexport function begin<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n>(\n stage:\n | PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>,\n adapter: RequestAdapter<AdapterExecutionResult, AdapterRequestConfig>\n): RequestChain<Out, AdapterExecutionResult, AdapterRequestConfig, [Out]> {\n const requestChain = new RequestChain<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n []\n >();\n requestChain.setRequestAdapter(adapter);\n return requestChain.next(stage);\n}\n\n/**\n * Type guard to check if a stage is a PipelineRequestStage.\n *\n * @template Out - The output type\n * @template AdapterExecutionResult - The adapter execution result type\n * @template AdapterRequestConfig - The request configuration type\n * @param stage - The stage to check\n * @returns True if the stage is a PipelineRequestStage\n */\nfunction isPipelineRequestStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n PrevOut = Out,\n>(\n stage:\n | PipelineRequestStage<\n AdapterExecutionResult,\n Out,\n AdapterRequestConfig,\n PrevOut\n >\n | PipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n PrevOut\n >\n): stage is PipelineRequestStage<\n AdapterExecutionResult,\n Out,\n AdapterRequestConfig,\n PrevOut\n> {\n return \"config\" in stage && !(\"request\" in stage);\n}\n\n/**\n * Type guard to check if a stage is a PipelineManagerStage.\n *\n * @template Out - The output type\n * @template AdapterExecutionResult - The adapter execution result type\n * @template AdapterRequestConfig - The request configuration type\n * @template PrevOut - The previous output type (defaults to Out)\n * @param stage - The stage to check\n * @returns True if the stage is a PipelineManagerStage\n */\nfunction isPipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n PrevOut = Out,\n>(\n stage:\n | PipelineRequestStage<\n AdapterExecutionResult,\n Out,\n AdapterRequestConfig,\n PrevOut\n >\n | PipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n PrevOut\n >\n): stage is PipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n PrevOut\n> {\n return \"request\" in stage && !(\"config\" in stage);\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/url-validator.ts","../src/request-adapter.ts","../src/request-manager.ts","../src/utils/retry-utils.ts","../src/utils/chunk-processor.ts","../src/request-chain.ts","../src/request-batch.ts"],"names":["previousEntity","previousResult","isPipelineRequestStage","isPipelineManagerStage","batch"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * URL validation utilities to prevent SSRF (Server-Side Request Forgery) attacks\n */\n\nexport interface UrlValidationOptions {\n /**\n * Allow private/internal IP addresses (default: false)\n * WARNING: Enabling this can expose your application to SSRF attacks\n */\n allowPrivateIPs?: boolean;\n\n /**\n * Allow localhost addresses (default: false)\n * WARNING: Enabling this can expose your application to SSRF attacks\n */\n allowLocalhost?: boolean;\n\n /**\n * Custom list of allowed protocols (default: ['http:', 'https:'])\n */\n allowedProtocols?: string[];\n\n /**\n * Disable URL validation entirely (default: false)\n * WARNING: This completely disables SSRF protection\n */\n disableValidation?: boolean;\n}\n\nexport class SSRFError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SSRFError\";\n }\n}\n\n/**\n * Private IP ranges that should be blocked to prevent SSRF attacks\n */\nconst PRIVATE_IP_RANGES = [\n // IPv4 private ranges\n /^10\\./,\n /^172\\.(1[6-9]|2[0-9]|3[01])\\./,\n /^192\\.168\\./,\n // IPv6 private ranges\n /^fc00:/i,\n /^fe80:/i,\n /^::1$/,\n /^fd/,\n];\n\n/**\n * Validates a URL to prevent SSRF attacks\n * @param url - The URL to validate\n * @param options - Validation options\n * @throws {SSRFError} If the URL is invalid or potentially dangerous\n */\nexport function validateUrl(\n url: string,\n options: UrlValidationOptions = {}\n): void {\n const {\n allowPrivateIPs = false,\n allowLocalhost = false,\n allowedProtocols = [\"http:\", \"https:\"],\n disableValidation = false,\n } = options;\n\n if (disableValidation) {\n return;\n }\n\n if (!url || typeof url !== \"string\") {\n throw new SSRFError(\"URL must be a non-empty string\");\n }\n\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(url);\n } catch {\n throw new SSRFError(`Invalid URL format: ${url}`);\n }\n\n // Validate protocol\n const protocol = parsedUrl.protocol.toLowerCase();\n if (!allowedProtocols.includes(protocol)) {\n throw new SSRFError(\n `Protocol \"${protocol}\" is not allowed. Only ${allowedProtocols.join(\", \")} are permitted.`\n );\n }\n\n // Check for localhost\n const hostname = parsedUrl.hostname.toLowerCase();\n // Normalize IPv6 addresses (remove brackets if present)\n const normalizedHostname = hostname.replace(/^\\[|\\]$/g, \"\");\n const isLocalhost =\n normalizedHostname === \"localhost\" ||\n normalizedHostname === \"127.0.0.1\" ||\n normalizedHostname === \"::1\" ||\n normalizedHostname.startsWith(\"127.\") ||\n normalizedHostname === \"0.0.0.0\";\n\n if (isLocalhost && !allowLocalhost) {\n throw new SSRFError(\n \"Localhost addresses are not allowed for security reasons. Set allowLocalhost=true to override.\"\n );\n }\n\n // Check for private IP ranges\n if (!allowPrivateIPs) {\n // Check IPv4 addresses first with specific error messages\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(normalizedHostname)) {\n const parts = normalizedHostname.split(\".\").map(Number);\n const [a, b] = parts;\n\n // 10.0.0.0/8\n if (a === 10) {\n throw new SSRFError(\n \"Private IP addresses (10.x.x.x) are not allowed for security reasons.\"\n );\n }\n\n // 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) {\n throw new SSRFError(\n \"Private IP addresses (172.16-31.x.x) are not allowed for security reasons.\"\n );\n }\n\n // 192.168.0.0/16\n if (a === 192 && b === 168) {\n throw new SSRFError(\n \"Private IP addresses (192.168.x.x) are not allowed for security reasons.\"\n );\n }\n\n // 169.254.0.0/16 (link-local)\n if (a === 169 && b === 254) {\n throw new SSRFError(\n \"Link-local addresses (169.254.x.x) are not allowed for security reasons.\"\n );\n }\n }\n\n // Check IPv6 and other private IP ranges using regex\n const isPrivateIP = PRIVATE_IP_RANGES.some((range) =>\n range.test(normalizedHostname)\n );\n\n if (isPrivateIP) {\n throw new SSRFError(\n \"Private/internal IP addresses are not allowed for security reasons. Set allowPrivateIPs=true to override.\"\n );\n }\n }\n}\n","import type { IRequestConfig } from \"./models/request-params\";\nimport { validateUrl, type UrlValidationOptions } from \"./utils/url-validator\";\n\n/**\n * Abstract base class for request adapters that handle HTTP requests.\n * Provides URL validation and a common interface for different HTTP client implementations.\n *\n * @template ExecutionResult - The type of result returned by the adapter's HTTP client\n * @template RequestConfig - The type of request configuration, must extend IRequestConfig\n *\n * @example\n * ```typescript\n * class MyAdapter extends RequestAdapter<Response> {\n * async createRequest(config: IRequestConfig): Promise<Response> {\n * // Implementation\n * }\n * }\n * ```\n */\nexport default abstract class RequestAdapter<\n ExecutionResult,\n RequestConfig extends IRequestConfig = IRequestConfig,\n> {\n /**\n * URL validation options used to prevent SSRF attacks\n */\n protected urlValidationOptions: UrlValidationOptions;\n\n /**\n * Creates a new RequestAdapter instance.\n *\n * @param urlValidationOptions - Options for URL validation to prevent SSRF attacks\n */\n constructor(urlValidationOptions: UrlValidationOptions = {}) {\n this.urlValidationOptions = urlValidationOptions;\n }\n\n /**\n * Creates and executes an HTTP request using the adapter's underlying HTTP client.\n * This method must be implemented by concrete adapter classes.\n *\n * @param requestConfig - The request configuration object\n * @returns A promise that resolves to the execution result\n */\n public abstract createRequest(\n requestConfig: RequestConfig\n ): Promise<ExecutionResult>;\n\n /**\n * Type-safe getter for the execution result.\n * Allows casting the result to a specific type.\n *\n * @template T - The desired result type\n * @param result - The execution result to cast\n * @returns The result cast to type T\n */\n public getResult<T extends ExecutionResult>(result: ExecutionResult): T {\n return result as T;\n }\n\n /**\n * Executes a request with URL validation.\n * Validates the URL to prevent SSRF attacks before creating the request.\n *\n * @param requestConfig - The request configuration object\n * @returns A promise that resolves to the execution result\n * @throws {SSRFError} If the URL is invalid or potentially dangerous\n */\n public executeRequest(\n requestConfig: RequestConfig\n ): Promise<ExecutionResult> {\n // Validate URL to prevent SSRF attacks\n validateUrl(requestConfig.url, this.urlValidationOptions);\n return this.createRequest(requestConfig);\n }\n}\n","import type RequestAdapter from \"./request-adapter\";\nimport type {\n PipelineManagerStage,\n PipelineRequestStage,\n} from \"./models/request-params\";\nimport type { ErrorHandler, ResultHandler } from \"./models/handlers\";\nimport type { IRequestConfig } from \"./models/request-params\";\n\n/**\n * Abstract base class for managing request pipelines and flows.\n * Provides functionality for chaining requests, error handling, and result processing.\n *\n * @template Out - The output type of the request flow\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template RequestConfig - The type of request configuration, must extend IRequestConfig\n */\nexport default abstract class RequestFlow<\n Out,\n AdapterExecutionResult = Out,\n RequestConfig extends IRequestConfig = IRequestConfig,\n> {\n /**\n * List of pipeline stages to execute\n */\n protected requestList: (\n | PipelineRequestStage<any, any, any>\n | PipelineManagerStage<any, any, any>\n )[] = [];\n /**\n * Optional error handler callback\n */\n protected errorHandler?: ErrorHandler;\n /**\n * Optional result handler callback\n */\n protected resultHandler?: ResultHandler<Out | Out[]>;\n /**\n * Optional finish handler callback executed after completion\n */\n protected finishHandler?: () => void;\n /**\n * The request adapter used to execute HTTP requests\n */\n protected adapter: RequestAdapter<AdapterExecutionResult, RequestConfig>;\n\n /**\n * Executes the request flow and returns the final result.\n * Must be implemented by concrete subclasses.\n *\n * @returns A promise that resolves to the output result\n */\n public abstract execute(): Promise<Out>;\n\n /**\n * Sets the request adapter to use for executing HTTP requests.\n *\n * @param adapter - The request adapter instance\n * @returns The current RequestFlow instance for method chaining\n */\n public setRequestAdapter(\n adapter: RequestAdapter<AdapterExecutionResult, RequestConfig>\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.adapter = adapter;\n return this;\n }\n\n /**\n * Adds multiple pipeline stages to the request list.\n *\n * @param requestList - Array of pipeline stages to add\n * @returns The current RequestFlow instance for method chaining\n */\n public addAll(\n requestList: Array<\n | PipelineRequestStage<AdapterExecutionResult, Out, RequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, RequestConfig>\n > = []\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.requestList = this.requestList.concat(requestList);\n return this;\n }\n\n /**\n * Sets an error handler callback that will be called when an error occurs during execution.\n *\n * @param errorHandler - Function to handle errors\n * @returns The current RequestFlow instance for method chaining\n */\n public withErrorHandler(\n errorHandler: ErrorHandler\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.errorHandler = errorHandler;\n return this;\n }\n\n /**\n * Sets a result handler callback that will be called with the execution result.\n *\n * @param resultHandler - Function to handle results\n * @returns The current RequestFlow instance for method chaining\n */\n public withResultHandler(\n resultHandler: ResultHandler<Out | Out[]>\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.resultHandler = resultHandler;\n return this;\n }\n\n /**\n * Sets a finish handler callback that will be called after execution completes (success or failure).\n *\n * @param finishHandler - Function to execute on completion\n * @returns The current RequestFlow instance for method chaining\n */\n public withFinishHandler(\n finishHandler: () => void\n ): RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n this.finishHandler = finishHandler;\n return this;\n }\n}\n","/**\n * Utility functions for retry logic and error handling.\n */\n\n/**\n * Attempts to extract HTTP status code from an error object.\n * Works with different adapter error formats (Axios, Fetch, etc.).\n *\n * @param error - The error object\n * @returns The HTTP status code if available, undefined otherwise\n */\nexport function getErrorStatus(error: Error): number | undefined {\n // Axios errors have response.status\n if (\n typeof (error as any).response !== \"undefined\" &&\n typeof (error as any).response.status === \"number\"\n ) {\n return (error as any).response.status;\n }\n\n // Some adapters might put status directly on error\n if (typeof (error as any).status === \"number\") {\n return (error as any).status;\n }\n\n // Check for statusCode (some libraries use this)\n if (typeof (error as any).statusCode === \"number\") {\n return (error as any).statusCode;\n }\n\n return undefined;\n}\n\n/**\n * Checks if an error is a network error (connection failure, timeout, etc.).\n *\n * @param error - The error object\n * @returns True if the error appears to be a network error\n */\nexport function isNetworkError(error: Error): boolean {\n // Common network error names\n const networkErrorNames = [\n \"TypeError\",\n \"NetworkError\",\n \"TimeoutError\",\n \"AbortError\",\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n ];\n\n if (networkErrorNames.includes(error.name)) {\n return true;\n }\n\n // Check error message for network-related keywords\n const networkKeywords = [\n \"network\",\n \"connection\",\n \"timeout\",\n \"fetch\",\n \"failed to fetch\",\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n ];\n\n const errorMessage = error.message?.toLowerCase() || \"\";\n return networkKeywords.some((keyword) => errorMessage.includes(keyword));\n}\n\n/**\n * Default retry condition that retries on network errors.\n * This is used when no retryCondition is provided in RetryConfig.\n *\n * @param error - The error that occurred\n * @returns True if the error is a network error\n */\nexport function defaultRetryCondition(error: Error): boolean {\n return isNetworkError(error);\n}\n\n/**\n * Helper function to create a retry condition that retries on specific HTTP status codes.\n *\n * @param statusCodes - Array of HTTP status codes to retry on\n * @returns A retry condition function\n *\n * @example\n * ```typescript\n * retry: {\n * retryCondition: retryOnStatusCodes(500, 502, 503, 504, 429)\n * }\n * ```\n */\nexport function retryOnStatusCodes(\n ...statusCodes: number[]\n): (error: Error) => boolean {\n return (error: Error) => {\n const status = getErrorStatus(error);\n return status !== undefined && statusCodes.includes(status);\n };\n}\n\n/**\n * Helper function to create a retry condition that retries on network errors OR specific status codes.\n *\n * @param statusCodes - Array of HTTP status codes to retry on\n * @returns A retry condition function\n *\n * @example\n * ```typescript\n * retry: {\n * retryCondition: retryOnNetworkOrStatusCodes(500, 502, 503, 504, 429)\n * }\n * ```\n */\nexport function retryOnNetworkOrStatusCodes(\n ...statusCodes: number[]\n): (error: Error, attempt: number) => boolean {\n return (error: Error) => {\n // Always retry network errors\n if (isNetworkError(error)) {\n return true;\n }\n // Retry on specified status codes\n const status = getErrorStatus(error);\n return status !== undefined && statusCodes.includes(status);\n };\n}\n","import type { ChunkHandler } from \"../models/handlers\";\nimport type { ChunkProcessingConfig } from \"../models/request-params\";\n\n/**\n * Processes a ReadableStream progressively, calling the chunk handler for each chunk.\n * Supports both text and binary streams.\n *\n * @template Chunk - The type of chunk data\n * @param stream - The ReadableStream to process\n * @param config - The chunk processing configuration\n * @returns A promise that resolves when processing is complete, optionally with accumulated data\n */\nexport async function processStream<Chunk = string | Uint8Array>(\n stream: ReadableStream<Uint8Array>,\n config: ChunkProcessingConfig<Chunk>\n): Promise<Chunk | undefined> {\n const { chunkHandler, encoding = \"utf-8\", accumulate = false } = config;\n\n const reader = stream.getReader();\n const decoder = new TextDecoder(encoding);\n let accumulatedData: string | Uint8Array | undefined;\n let chunkIndex = 0;\n let totalBytesRead = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n // Process any remaining accumulated data\n if (accumulatedData !== undefined && accumulatedData.length > 0) {\n await processChunk(\n accumulatedData as Chunk,\n chunkHandler,\n chunkIndex,\n true,\n totalBytesRead\n );\n }\n break;\n }\n\n totalBytesRead += value.length;\n\n if (accumulate) {\n // Accumulate chunks\n if (accumulatedData === undefined) {\n accumulatedData = value;\n } else if (typeof accumulatedData === \"string\") {\n accumulatedData += decoder.decode(value, { stream: true });\n } else {\n // Concatenate Uint8Arrays\n const combined = new Uint8Array(\n accumulatedData.length + value.length\n );\n combined.set(accumulatedData);\n combined.set(value, accumulatedData.length);\n accumulatedData = combined;\n }\n }\n\n // Process chunk\n const isLast = false; // We don't know if it's the last until done\n await processChunk(\n value as Chunk,\n chunkHandler,\n chunkIndex,\n isLast,\n totalBytesRead\n );\n\n chunkIndex++;\n }\n\n return accumulatedData as Chunk | undefined;\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Processes a text stream line by line (useful for NDJSON, CSV, or line-delimited data).\n *\n * @param stream - The ReadableStream to process\n * @param config - The chunk processing configuration\n * @returns A promise that resolves when processing is complete\n */\nexport async function processTextStreamLineByLine(\n stream: ReadableStream<Uint8Array>,\n config: ChunkProcessingConfig<string>\n): Promise<string | undefined> {\n const { chunkHandler, encoding = \"utf-8\", accumulate = false } = config;\n\n const reader = stream.getReader();\n const decoder = new TextDecoder(encoding);\n let buffer = \"\";\n let lineIndex = 0;\n let totalBytesRead = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n // Process any remaining buffer content\n if (buffer.length > 0) {\n await processChunk(\n buffer,\n chunkHandler,\n lineIndex,\n true,\n totalBytesRead\n );\n }\n break;\n }\n\n totalBytesRead += value.length;\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.length > 0) {\n // Only process non-empty lines\n await processChunk(\n line,\n chunkHandler,\n lineIndex,\n false,\n totalBytesRead\n );\n lineIndex++;\n }\n }\n }\n\n return accumulate ? buffer : undefined;\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Processes a Response body as a stream with chunk processing.\n * Automatically detects if the response has a readable stream and processes it.\n *\n * @template Chunk - The type of chunk data\n * @param response - The Response object\n * @param config - The chunk processing configuration\n * @returns A promise that resolves with the processed result or the original response\n */\nexport async function processResponseStream<Chunk = string | Uint8Array>(\n response: Response,\n config: ChunkProcessingConfig<Chunk>\n): Promise<Response | Chunk> {\n if (!response.body) {\n throw new Error(\"Response body is not available for streaming\");\n }\n\n const processed = await processStream(response.body, config);\n\n // If accumulation is enabled, return the accumulated data\n // Otherwise, return the original response (chunks were processed via handler)\n if (config.accumulate && processed !== undefined) {\n return processed;\n }\n\n // Return a new Response with the processed data if needed\n // For now, return original response since chunks were handled via handler\n return response;\n}\n\n/**\n * Helper function to process a single chunk with the handler.\n *\n * @template Chunk - The type of chunk data\n * @param chunk - The chunk to process\n * @param handler - The chunk handler function\n * @param index - The chunk index\n * @param isLast - Whether this is the last chunk\n * @param totalBytesRead - Total bytes read so far\n */\nasync function processChunk<Chunk>(\n chunk: Chunk,\n handler: ChunkHandler<Chunk>,\n index: number,\n isLast: boolean,\n totalBytesRead: number\n): Promise<void> {\n const result = handler(chunk, {\n index,\n isLast,\n totalBytesRead,\n });\n\n if (result instanceof Promise) {\n await result;\n }\n}\n\n/**\n * Checks if a value is a ReadableStream.\n *\n * @param value - The value to check\n * @returns True if the value is a ReadableStream\n */\nexport function isReadableStream(\n value: unknown\n): value is ReadableStream<Uint8Array> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"getReader\" in value &&\n typeof (value as ReadableStream).getReader === \"function\"\n );\n}\n\n/**\n * Checks if a Response has a readable body stream.\n *\n * @param response - The Response to check\n * @returns True if the response has a readable body stream\n */\nexport function hasReadableStream(response: Response): boolean {\n return response.body !== null && isReadableStream(response.body);\n}\n","import type RequestAdapter from \"./request-adapter\";\nimport RequestFlow from \"./request-manager\";\nimport type {\n IRequestConfig,\n PipelineRequestStage,\n PipelineManagerStage,\n RetryConfig,\n} from \"./models/request-params\";\nimport { defaultRetryCondition } from \"./utils/retry-utils\";\nimport {\n processResponseStream,\n hasReadableStream,\n} from \"./utils/chunk-processor\";\n\n/**\n * A chainable request pipeline that allows sequential execution of HTTP requests.\n * Each stage can depend on the result of the previous stage, and stages can be conditionally executed.\n *\n * @template Out - The output type of the current chain\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template AdapterRequestConfig - The type of request configuration\n * @template Types - Tuple type tracking all output types in the chain\n *\n * @example\n * ```typescript\n * const chain = RequestChain.begin(\n * { config: { url: 'https://api.example.com/users', method: 'GET' } },\n * adapter\n * ).next({ config: { url: 'https://api.example.com/posts', method: 'GET' } });\n *\n * const result = await chain.execute();\n * ```\n */\nexport default class RequestChain<\n Out,\n AdapterExecutionResult = Out,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n Types extends readonly unknown[] = [Out],\n> extends RequestFlow<Out, AdapterExecutionResult, AdapterRequestConfig> {\n // #region Public methods\n\n /**\n * Creates a new RequestChain with an initial stage.\n * This is the entry point for building a request chain.\n *\n * @template Out - The output type of the initial stage\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template AdapterRequestConfig - The type of request configuration\n * @param stage - The initial pipeline stage (request or manager stage)\n * @param adapter - The request adapter to use for HTTP requests\n * @returns A new RequestChain instance with the initial stage\n */\n public static begin = <\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n >(\n stage:\n | PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>,\n adapter: RequestAdapter<AdapterExecutionResult, AdapterRequestConfig>\n ): RequestChain<Out, AdapterExecutionResult, AdapterRequestConfig, [Out]> => {\n const requestChain = new RequestChain<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n []\n >();\n requestChain.setRequestAdapter(adapter);\n return requestChain.next(stage);\n };\n\n /**\n * Adds a new stage to the request chain and returns a new chain with updated types.\n * This method enables type-safe chaining of requests.\n *\n * @template NewOut - The output type of the new stage\n * @param stage - The pipeline stage to add (request or manager stage)\n * @returns A new RequestChain instance with the added stage\n */\n public next = <NewOut>(\n stage:\n | PipelineRequestStage<\n AdapterExecutionResult,\n NewOut,\n AdapterRequestConfig,\n Out\n >\n | PipelineManagerStage<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n Out\n >\n ): RequestChain<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n [...Types, NewOut]\n > => {\n return this.addRequestEntity(stage);\n };\n\n /**\n * Executes all stages in the chain sequentially and returns the final result.\n * Handles errors and calls registered handlers appropriately.\n *\n * @returns A promise that resolves to the final output result\n * @throws {Error} If an error occurs and no error handler is registered\n */\n public execute = async (): Promise<Out> => {\n try {\n const results: Out[] = await this.executeAllRequests(this.requestList);\n const result: Out = results[results.length - 1];\n if (this.resultHandler && result) {\n this.resultHandler(result);\n }\n return result;\n } catch (error) {\n if (this.errorHandler) {\n this.errorHandler(error);\n return Promise.reject(error);\n } else {\n throw error;\n }\n } finally {\n if (this.finishHandler) {\n this.finishHandler();\n }\n }\n };\n\n /**\n * Executes all stages in the chain and returns all results as a tuple.\n * Useful when you need access to intermediate results.\n *\n * @returns A promise that resolves to a tuple of all stage results\n * @throws {Error} If an error occurs and no error handler is registered\n */\n public async executeAll(): Promise<Types> {\n try {\n const results = await this.executeAllRequests(this.requestList);\n if (this.resultHandler && results.length > 0) {\n this.resultHandler(results);\n }\n return results as unknown as Types;\n } catch (error) {\n if (this.errorHandler) {\n this.errorHandler(error);\n return Promise.reject(error);\n } else {\n throw error;\n }\n } finally {\n if (this.finishHandler) {\n this.finishHandler();\n }\n }\n }\n\n // #endregion\n\n // #region Private methods\n\n /**\n * Adds a request entity (stage) to the internal request list.\n *\n * @template NewOut - The output type of the new stage\n * @param stage - The pipeline stage to add\n * @returns A new RequestChain instance with updated types\n */\n private addRequestEntity = <NewOut, PrevOut = Out>(\n stage:\n | PipelineRequestStage<\n AdapterExecutionResult,\n NewOut,\n AdapterRequestConfig,\n PrevOut\n >\n | PipelineManagerStage<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n PrevOut\n >\n ): RequestChain<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n [...Types, NewOut]\n > => {\n this.requestList.push(stage);\n return this as unknown as RequestChain<\n NewOut,\n AdapterExecutionResult,\n AdapterRequestConfig,\n [...Types, NewOut]\n >;\n };\n\n /**\n * Executes all request entities in sequence, handling preconditions and mappers.\n * Stages with failed preconditions are skipped but preserve the previous result.\n *\n * @template Out - The output type\n * @param requestEntityList - List of pipeline stages to execute\n * @returns A promise that resolves to an array of all stage results\n */\n private executeAllRequests = async <Out>(\n requestEntityList: (\n | PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>\n )[]\n ): Promise<Out[]> => {\n const results: Out[] = [];\n for (let i = 0; i < requestEntityList.length; i++) {\n const requestEntity:\n | PipelineRequestStage<\n AdapterExecutionResult,\n Out,\n AdapterRequestConfig\n >\n | PipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig\n > = requestEntityList[i];\n\n // Check precondition - skip stage if precondition returns false\n if (requestEntity.precondition && !requestEntity.precondition()) {\n const previousEntity = requestEntityList[i - 1];\n const previousResult: Out | undefined = previousEntity?.result;\n // Use previous result or undefined if this is the first stage\n // Don't push to results - skipped stages don't produce new results\n requestEntityList[i].result = previousResult as Out | undefined;\n continue;\n }\n\n const previousEntity = requestEntityList[i - 1];\n const previousResult: Out | undefined = previousEntity?.result;\n try {\n const requestResult: Out = await this.executeSingle<Out>(\n requestEntity,\n previousResult\n );\n let result: Out = requestResult;\n if (requestEntity.mapper) {\n let mappedResult: Out | Promise<Out>;\n if (isPipelineRequestStage(requestEntity)) {\n mappedResult = requestEntity.mapper(\n requestResult as unknown as AdapterExecutionResult,\n previousResult\n );\n } else if (isPipelineManagerStage(requestEntity)) {\n mappedResult = requestEntity.mapper(\n requestResult as unknown as Out,\n previousResult\n );\n } else {\n mappedResult = result;\n }\n result =\n mappedResult instanceof Promise ? await mappedResult : mappedResult;\n }\n if (requestEntity.resultInterceptor) {\n await requestEntity.resultInterceptor(result);\n }\n requestEntityList[i].result = result as Out;\n results.push(result);\n } catch (error) {\n const requestConfig = isPipelineRequestStage(requestEntity)\n ? requestEntity.config\n : undefined;\n error.cause = { ...error.cause, requestConfig };\n if (requestEntity.errorHandler) {\n await requestEntity.errorHandler(error);\n }\n throw error;\n }\n }\n return results;\n };\n\n /**\n * Executes a single request entity (stage).\n * Handles both request stages and nested manager stages.\n * Implements retry logic for request stages when retry configuration is provided.\n * Supports progressive chunk processing for streaming responses.\n *\n * @template Out - The output type\n * @param requestEntity - The pipeline stage to execute\n * @param previousResult - The result from the previous stage (optional)\n * @returns A promise that resolves to the stage result\n * @throws {Error} If the stage type is unknown or all retries are exhausted\n */\n private executeSingle = async <Out>(\n requestEntity:\n | PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>,\n previousResult?: Out\n ): Promise<Out> => {\n if (isPipelineRequestStage(requestEntity)) {\n const { config, retry, chunkProcessing } = requestEntity;\n const requestConfig: AdapterRequestConfig =\n typeof config === \"function\"\n ? (config(previousResult as Out) as AdapterRequestConfig)\n : (config as AdapterRequestConfig);\n\n // If retry config is provided, wrap execution in retry logic\n if (retry) {\n return this.executeWithRetry<Out>(\n requestConfig,\n retry,\n chunkProcessing\n );\n }\n\n // Execute request and handle chunk processing if enabled\n const rawResult: AdapterExecutionResult =\n await this.adapter.executeRequest(requestConfig);\n return this.processResultWithChunks<Out>(rawResult, chunkProcessing);\n } else if (isPipelineManagerStage(requestEntity)) {\n const { request } = requestEntity;\n const rawResult: Out = await request.execute();\n // For nested managers, the result is already processed, so we return it directly\n // The adapter's getResult expects AdapterExecutionResult, but nested results are already Out\n return rawResult;\n } else {\n throw new Error(\"Unknown type\");\n }\n };\n\n /**\n * Executes a request with retry logic based on the provided retry configuration.\n * Supports chunk processing for streaming responses.\n *\n * @template Out - The output type\n * @param requestConfig - The request configuration\n * @param retryConfig - The retry configuration\n * @param chunkProcessing - Optional chunk processing configuration\n * @returns A promise that resolves to the request result\n * @throws {Error} If all retry attempts are exhausted\n */\n private executeWithRetry = async <Out>(\n requestConfig: AdapterRequestConfig,\n retryConfig: RetryConfig,\n chunkProcessing?: import(\"./models/request-params\").ChunkProcessingConfig\n ): Promise<Out> => {\n const maxRetries = retryConfig.maxRetries ?? 3;\n const retryCondition = retryConfig.retryCondition ?? defaultRetryCondition;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const rawResult: AdapterExecutionResult =\n await this.adapter.executeRequest(requestConfig);\n return this.processResultWithChunks<Out>(rawResult, chunkProcessing);\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Check if we should retry\n const shouldRetry =\n attempt < maxRetries && retryCondition(lastError, attempt);\n\n if (!shouldRetry) {\n throw lastError;\n }\n\n // Calculate delay before retrying\n const delay = this.calculateRetryDelay(\n attempt + 1,\n lastError,\n retryConfig\n );\n\n // Wait before retrying\n if (delay > 0) {\n await this.sleep(delay);\n }\n }\n }\n\n // This should never be reached, but TypeScript needs it\n throw lastError || new Error(\"Retry failed\");\n };\n\n /**\n * Processes a result with chunk processing if enabled.\n * Handles streaming responses by processing chunks progressively.\n *\n * @template Out - The output type\n * @param rawResult - The raw result from the adapter\n * @param chunkProcessing - Optional chunk processing configuration\n * @returns A promise that resolves to the processed result\n */\n private processResultWithChunks = async <Out>(\n rawResult: AdapterExecutionResult,\n chunkProcessing?: import(\"./models/request-params\").ChunkProcessingConfig\n ): Promise<Out> => {\n // If chunk processing is enabled and result is a Response with readable stream\n if (\n chunkProcessing?.enabled &&\n rawResult instanceof Response &&\n hasReadableStream(rawResult)\n ) {\n // Clone the response to avoid consuming the original stream\n const clonedResponse = rawResult.clone();\n\n // Process the stream\n const processed = await processResponseStream(clonedResponse, {\n ...chunkProcessing,\n });\n\n // If accumulation is enabled, return the accumulated data\n // Otherwise, return the original response (chunks were processed via handler)\n if (chunkProcessing.accumulate && processed !== rawResult) {\n return processed as unknown as Out;\n }\n\n // Return original response if chunks were only processed via handler\n return this.adapter.getResult(rawResult) as unknown as Out;\n }\n\n // No chunk processing, return result normally\n return this.adapter.getResult(rawResult) as unknown as Out;\n };\n\n /**\n * Calculates the delay before the next retry attempt.\n *\n * @param attempt - The current attempt number (1-indexed for retries)\n * @param error - The error that occurred\n * @param retryConfig - The retry configuration\n * @returns The delay in milliseconds\n */\n private calculateRetryDelay(\n attempt: number,\n error: Error,\n retryConfig: RetryConfig\n ): number {\n const baseDelay = retryConfig.retryDelay ?? 1000;\n const maxDelay = retryConfig.maxDelay;\n\n let delay: number;\n\n if (typeof baseDelay === \"function\") {\n // Custom delay function\n delay = baseDelay(attempt, error);\n } else if (retryConfig.exponentialBackoff) {\n // Exponential backoff: delay * 2^attempt\n delay = baseDelay * Math.pow(2, attempt - 1);\n // Apply maxDelay cap if provided\n if (maxDelay !== undefined && delay > maxDelay) {\n delay = maxDelay;\n }\n } else {\n // Fixed delay\n delay = baseDelay;\n }\n\n return Math.max(0, delay);\n }\n\n /**\n * Sleeps for the specified number of milliseconds.\n *\n * @param ms - Milliseconds to sleep\n * @returns A promise that resolves after the delay\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // #endregion\n}\n\n/**\n * Creates a new RequestChain with an initial stage.\n * This is a convenience function that wraps RequestChain.begin.\n *\n * @template Out - The output type of the initial stage\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template AdapterRequestConfig - The type of request configuration\n * @param stage - The initial pipeline stage (request or manager stage)\n * @param adapter - The request adapter to use for HTTP requests\n * @returns A new RequestChain instance with the initial stage\n */\nexport function begin<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n>(\n stage:\n | PipelineRequestStage<AdapterExecutionResult, Out, AdapterRequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, AdapterRequestConfig>,\n adapter: RequestAdapter<AdapterExecutionResult, AdapterRequestConfig>\n): RequestChain<Out, AdapterExecutionResult, AdapterRequestConfig, [Out]> {\n const requestChain = new RequestChain<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n []\n >();\n requestChain.setRequestAdapter(adapter);\n return requestChain.next(stage);\n}\n\n/**\n * Type guard to check if a stage is a PipelineRequestStage.\n *\n * @template Out - The output type\n * @template AdapterExecutionResult - The adapter execution result type\n * @template AdapterRequestConfig - The request configuration type\n * @param stage - The stage to check\n * @returns True if the stage is a PipelineRequestStage\n */\nfunction isPipelineRequestStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n PrevOut = Out,\n>(\n stage:\n | PipelineRequestStage<\n AdapterExecutionResult,\n Out,\n AdapterRequestConfig,\n PrevOut\n >\n | PipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n PrevOut\n >\n): stage is PipelineRequestStage<\n AdapterExecutionResult,\n Out,\n AdapterRequestConfig,\n PrevOut\n> {\n return \"config\" in stage && !(\"request\" in stage);\n}\n\n/**\n * Type guard to check if a stage is a PipelineManagerStage.\n *\n * @template Out - The output type\n * @template AdapterExecutionResult - The adapter execution result type\n * @template AdapterRequestConfig - The request configuration type\n * @template PrevOut - The previous output type (defaults to Out)\n * @param stage - The stage to check\n * @returns True if the stage is a PipelineManagerStage\n */\nfunction isPipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig extends IRequestConfig = IRequestConfig,\n PrevOut = Out,\n>(\n stage:\n | PipelineRequestStage<\n AdapterExecutionResult,\n Out,\n AdapterRequestConfig,\n PrevOut\n >\n | PipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n PrevOut\n >\n): stage is PipelineManagerStage<\n Out,\n AdapterExecutionResult,\n AdapterRequestConfig,\n PrevOut\n> {\n return \"request\" in stage && !(\"config\" in stage);\n}\n","import RequestFlow from \"./request-manager\";\nimport type RequestAdapter from \"./request-adapter\";\nimport type {\n IRequestConfig,\n PipelineRequestStage,\n PipelineManagerStage,\n RetryConfig,\n ChunkProcessingConfig,\n} from \"./models/request-params\";\nimport { defaultRetryCondition } from \"./utils/retry-utils\";\nimport {\n processResponseStream,\n hasReadableStream,\n} from \"./utils/chunk-processor\";\n\n/**\n * Type helper to extract the output type from a pipeline stage.\n */\ntype ExtractStageOutput<\n Stage extends\n | PipelineRequestStage<any, any, any>\n | PipelineManagerStage<any, any, any>,\n> =\n Stage extends PipelineRequestStage<any, infer Out, any>\n ? Out\n : Stage extends PipelineManagerStage<infer Out, any, any>\n ? Out\n : never;\n\n/**\n * Type helper to convert an array of stages into a tuple of their output types.\n * This enables heterogeneous batch requests where each request can return a different type.\n */\ntype StagesToTuple<\n Stages extends readonly (\n | PipelineRequestStage<any, any, any>\n | PipelineManagerStage<any, any, any>\n )[],\n> = {\n readonly [K in keyof Stages]: ExtractStageOutput<Stages[K]>;\n};\n\n/**\n * A batch request manager that executes multiple requests in parallel (or with a concurrency limit).\n * All requests are executed simultaneously (or in controlled batches), and results are returned as an array or tuple.\n *\n * Supports both homogeneous batches (all requests return the same type) and heterogeneous batches\n * (each request can return a different type, using tuple types for type safety).\n *\n * @template Out - The output type:\n * - For homogeneous batches: an array type (e.g., `User[]`)\n * - For heterogeneous batches: a tuple type (e.g., `[User, Product, Order]`)\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template RequestConfig - The type of request configuration\n *\n * @example\n * ```typescript\n * // Homogeneous batch - each request returns a User\n * const batch = new RequestBatch<User[], Response, FetchRequestConfig>();\n * batch.setRequestAdapter(adapter);\n * batch.addAll([\n * { config: { url: '/api/users/1', method: 'GET' } },\n * { config: { url: '/api/users/2', method: 'GET' } },\n * { config: { url: '/api/users/3', method: 'GET' } }\n * ]);\n * const results: User[] = await batch.execute();\n *\n * // Heterogeneous batch - each request returns a different type\n * const heterogeneousBatch = RequestBatch.batch(\n * [\n * { config: { url: '/api/users/1', method: 'GET' }, mapper: (r) => r.json() as Promise<User> },\n * { config: { url: '/api/products/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Product> },\n * { config: { url: '/api/orders/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Order> }\n * ],\n * adapter\n * );\n * const results: [User, Product, Order] = await heterogeneousBatch.execute();\n * ```\n */\nexport class RequestBatch<\n Out,\n AdapterExecutionResult = Out,\n RequestConfig extends IRequestConfig = IRequestConfig,\n> extends RequestFlow<Out, AdapterExecutionResult, RequestConfig> {\n /**\n * Creates a new RequestBatch with stages and adapter.\n * This is the entry point for building a request batch.\n *\n * Supports both homogeneous batches (all requests return the same type) and\n * heterogeneous batches (each request can return a different type, using tuple types).\n *\n * @template Out - The output type (should be an array type, e.g., `User[]` for homogeneous batches)\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template RequestConfig - The type of request configuration\n * @param stages - Array of pipeline stages (request or manager stages)\n * @param adapter - The request adapter to use for HTTP requests\n * @returns A new RequestBatch instance with the stages and adapter configured\n *\n * @example\n * ```typescript\n * // Homogeneous batch - all requests return User\n * const batch = RequestBatch.batch(\n * [\n * { config: { url: '/api/users/1', method: 'GET' } },\n * { config: { url: '/api/users/2', method: 'GET' } },\n * { config: { url: '/api/users/3', method: 'GET' } }\n * ],\n * adapter\n * );\n * const results: User[] = await batch.execute();\n *\n * // Heterogeneous batch - each request returns a different type\n * const heterogeneousBatch = RequestBatch.batch(\n * [\n * { config: { url: '/api/users/1', method: 'GET' }, mapper: (r) => r.json() as Promise<User> },\n * { config: { url: '/api/products/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Product> },\n * { config: { url: '/api/orders/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Order> }\n * ],\n * adapter\n * );\n * const results: [User, Product, Order] = await heterogeneousBatch.execute();\n * ```\n */\n public static batch = <\n Stages extends readonly (\n | PipelineRequestStage<AdapterExecutionResult, any, RequestConfig>\n | PipelineManagerStage<any, AdapterExecutionResult, RequestConfig>\n )[],\n AdapterExecutionResult,\n RequestConfig extends IRequestConfig = IRequestConfig,\n >(\n stages: Stages,\n adapter: RequestAdapter<AdapterExecutionResult, RequestConfig>\n ): RequestBatch<\n StagesToTuple<Stages>,\n AdapterExecutionResult,\n RequestConfig\n > => {\n const batch = new RequestBatch<\n StagesToTuple<Stages>,\n AdapterExecutionResult,\n RequestConfig\n >();\n batch.setRequestAdapter(adapter);\n // Type assertion needed: stages have individual types, but we store them as any for internal processing\n // Convert readonly array to mutable array for addAll\n batch.addAll([...stages] as Array<\n | PipelineRequestStage<\n AdapterExecutionResult,\n StagesToTuple<Stages>[number],\n RequestConfig\n >\n | PipelineManagerStage<\n StagesToTuple<Stages>[number],\n AdapterExecutionResult,\n RequestConfig\n >\n >);\n return batch;\n };\n\n /**\n * Maximum number of concurrent requests to execute at the same time.\n * If undefined, all requests will execute in parallel (default behavior).\n */\n private concurrency?: number;\n\n /**\n * Sets the maximum number of concurrent requests to execute simultaneously.\n * If not set, all requests will execute in parallel.\n *\n * @param limit - Maximum number of concurrent requests (must be > 0)\n * @returns The current RequestBatch instance for method chaining\n * @throws {Error} If limit is less than or equal to 0\n *\n * @example\n * ```typescript\n * const batch = new RequestBatch<User[], Response, FetchRequestConfig>();\n * batch.setRequestAdapter(adapter);\n * batch.withConcurrency(5); // Execute max 5 requests at a time\n * batch.addAll([...requests]);\n * const results = await batch.execute();\n * ```\n */\n public withConcurrency(\n limit: number\n ): RequestBatch<Out, AdapterExecutionResult, RequestConfig> {\n if (limit <= 0) {\n throw new Error(\"Concurrency limit must be greater than 0\");\n }\n this.concurrency = limit;\n return this;\n }\n\n /**\n * Executes all requests in the batch in parallel (or with concurrency limit if set) and returns all results.\n * Handles errors and calls registered handlers appropriately.\n *\n * For homogeneous batches, `Out` should be an array type (e.g., `User[]`).\n * For heterogeneous batches, `Out` should be a tuple type (e.g., `[User, Product, Order]`).\n *\n * @returns A promise that resolves to an array or tuple of all results (typed as Out)\n * @throws {Error} If an error occurs and no error handler is registered\n */\n public execute = async (): Promise<Out> => {\n try {\n // Execute all requests - results will be in order\n const results = await this.executeAllRequestsHeterogeneous(\n this.requestList\n );\n if (this.resultHandler && results.length > 0) {\n this.resultHandler(results as Out | Out[]);\n }\n // Cast to Out - preserves tuple types for heterogeneous batches\n return results as unknown as Out;\n } catch (error) {\n if (this.errorHandler) {\n this.errorHandler(error);\n return Promise.reject(error);\n } else {\n throw error;\n }\n } finally {\n if (this.finishHandler) {\n this.finishHandler();\n }\n }\n };\n\n /**\n * Executes all request entities in parallel (or with concurrency limit if set), handling preconditions and mappers.\n * Stages with failed preconditions are skipped.\n * This method preserves individual types for each stage, enabling heterogeneous batches.\n *\n * @param requestEntityList - List of pipeline stages to execute\n * @returns A promise that resolves to an array of all stage results (preserves tuple types)\n */\n private executeAllRequestsHeterogeneous = async (\n requestEntityList: (\n | PipelineRequestStage<AdapterExecutionResult, any, RequestConfig>\n | PipelineManagerStage<any, AdapterExecutionResult, RequestConfig>\n )[]\n ): Promise<any[]> => {\n // Filter out stages that don't meet their preconditions\n const stagesToExecute = requestEntityList.filter(\n (stage) => !stage.precondition || stage.precondition()\n );\n\n // Track original indices to preserve order (important for tuple types)\n const stageIndices = stagesToExecute.map((_, index) => {\n const originalIndex = requestEntityList.indexOf(stagesToExecute[index]);\n return originalIndex >= 0 ? originalIndex : index;\n });\n\n // Create promise factories (not promises yet) to control when they start\n const promiseFactories = stagesToExecute.map(\n (requestEntity, localIndex) => async () => {\n try {\n // Execute the stage - type is inferred from the stage itself\n const result = await this.executeSingleHeterogeneous(\n requestEntity,\n undefined\n );\n\n // Apply mapper if provided\n let mappedResult = result;\n if (requestEntity.mapper) {\n let mapperResult: any;\n if (isPipelineRequestStage(requestEntity)) {\n mapperResult = requestEntity.mapper(\n result as unknown as AdapterExecutionResult,\n undefined\n );\n } else if (isPipelineManagerStage(requestEntity)) {\n mapperResult = requestEntity.mapper(\n result as unknown as any,\n undefined\n );\n } else {\n mapperResult = result;\n }\n\n mappedResult =\n mapperResult instanceof Promise\n ? await mapperResult\n : mapperResult;\n }\n\n // Apply result interceptor if provided\n if (requestEntity.resultInterceptor) {\n await requestEntity.resultInterceptor(mappedResult);\n }\n\n // Store result on the entity\n requestEntity.result = mappedResult;\n return { index: stageIndices[localIndex], result: mappedResult };\n } catch (error) {\n const requestConfig = isPipelineRequestStage(requestEntity)\n ? requestEntity.config\n : undefined;\n error.cause = { ...error.cause, requestConfig };\n if (requestEntity.errorHandler) {\n await requestEntity.errorHandler(error);\n }\n throw error;\n }\n }\n );\n\n // Execute with concurrency limit if set, otherwise execute all in parallel\n let results: Array<{ index: number; result: any }>;\n if (this.concurrency !== undefined && this.concurrency > 0) {\n results = await this.executeWithConcurrencyLimitHeterogeneous(\n promiseFactories,\n this.concurrency\n );\n } else {\n // Execute all promises in parallel\n const promises = promiseFactories.map((factory) => factory());\n results = await Promise.all(promises);\n }\n\n // Sort results by original index to preserve order (critical for tuple types)\n results.sort((a, b) => a.index - b.index);\n\n // Return results in the correct order, preserving tuple structure\n return results.map((r) => r.result);\n };\n\n /**\n * Executes promise factories with a concurrency limit.\n * Processes promises in batches, ensuring only a limited number run concurrently.\n * This version preserves index information for tuple type support.\n *\n * @param promiseFactories - Array of functions that return promises when called\n * @param limit - Maximum number of concurrent promises to execute\n * @returns A promise that resolves to an array of results with index information\n */\n private executeWithConcurrencyLimitHeterogeneous = async (\n promiseFactories: Array<() => Promise<{ index: number; result: any }>>,\n limit: number\n ): Promise<Array<{ index: number; result: any }>> => {\n const results: Array<{ index: number; result: any }> = new Array(\n promiseFactories.length\n );\n let currentIndex = 0;\n let completedCount = 0;\n let activeCount = 0;\n\n return new Promise<Array<{ index: number; result: any }>>(\n (resolve, reject) => {\n // Start the next promise if we haven't exceeded the limit\n const startNext = () => {\n // Don't start more if we've reached the limit or run out of promises\n if (activeCount >= limit || currentIndex >= promiseFactories.length) {\n return;\n }\n\n const localIndex = currentIndex++;\n const factory = promiseFactories[localIndex];\n activeCount++;\n\n // Execute the promise factory\n factory()\n .then((result) => {\n results[localIndex] = result;\n completedCount++;\n activeCount--;\n\n // If all promises are completed, resolve\n if (completedCount === promiseFactories.length) {\n resolve(results);\n } else {\n // Otherwise, start the next promise\n startNext();\n }\n })\n .catch((error) => {\n activeCount--;\n reject(error);\n });\n };\n\n // Start initial batch up to the concurrency limit\n for (let i = 0; i < Math.min(limit, promiseFactories.length); i++) {\n startNext();\n }\n }\n );\n };\n\n /**\n * Executes a single request entity (stage).\n * Handles both request stages and nested manager stages.\n * This version supports heterogeneous types by inferring the type from the stage.\n *\n * @param requestEntity - The pipeline stage to execute\n * @param previousResult - The result from the previous stage (optional, not used in batch)\n * @returns A promise that resolves to the stage result (type inferred from stage)\n * @throws {Error} If the stage type is unknown or all retries are exhausted\n */\n private executeSingleHeterogeneous = async (\n requestEntity:\n | PipelineRequestStage<AdapterExecutionResult, any, RequestConfig>\n | PipelineManagerStage<any, AdapterExecutionResult, RequestConfig>,\n previousResult?: any\n ): Promise<any> => {\n if (isPipelineRequestStage(requestEntity)) {\n const { config, retry, chunkProcessing } = requestEntity;\n const requestConfig: RequestConfig =\n typeof config === \"function\"\n ? (config(previousResult) as RequestConfig)\n : (config as RequestConfig);\n\n // If retry config is provided, wrap execution in retry logic\n if (retry) {\n return this.executeWithRetry<any>(\n requestConfig,\n retry,\n chunkProcessing\n );\n }\n\n // Execute request and handle chunk processing if enabled\n const rawResult: AdapterExecutionResult =\n await this.adapter.executeRequest(requestConfig);\n return this.processResultWithChunks<any>(rawResult, chunkProcessing);\n } else if (isPipelineManagerStage(requestEntity)) {\n const { request } = requestEntity;\n const rawResult = await request.execute();\n return rawResult;\n } else {\n throw new Error(\"Unknown type\");\n }\n };\n\n /**\n * Executes a request with retry logic based on the provided retry configuration.\n * Supports chunk processing for streaming responses.\n *\n * @template Out - The output type\n * @param requestConfig - The request configuration\n * @param retryConfig - The retry configuration\n * @param chunkProcessing - Optional chunk processing configuration\n * @returns A promise that resolves to the request result\n * @throws {Error} If all retry attempts are exhausted\n */\n private executeWithRetry = async <Out>(\n requestConfig: RequestConfig,\n retryConfig: RetryConfig,\n chunkProcessing?: ChunkProcessingConfig\n ): Promise<Out> => {\n const maxRetries = retryConfig.maxRetries ?? 3;\n const retryCondition = retryConfig.retryCondition ?? defaultRetryCondition;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const rawResult: AdapterExecutionResult =\n await this.adapter.executeRequest(requestConfig);\n return this.processResultWithChunks<Out>(rawResult, chunkProcessing);\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Check if we should retry\n const shouldRetry =\n attempt < maxRetries && retryCondition(lastError, attempt);\n\n if (!shouldRetry) {\n throw lastError;\n }\n\n // Calculate delay before retrying\n const delay = this.calculateRetryDelay(\n attempt + 1,\n lastError,\n retryConfig\n );\n\n // Wait before retrying\n if (delay > 0) {\n await this.sleep(delay);\n }\n }\n }\n\n // This should never be reached, but TypeScript needs it\n throw lastError || new Error(\"Retry failed\");\n };\n\n /**\n * Processes a result with chunk processing if enabled.\n * Handles streaming responses by processing chunks progressively.\n *\n * @template Out - The output type\n * @param rawResult - The raw result from the adapter\n * @param chunkProcessing - Optional chunk processing configuration\n * @returns A promise that resolves to the processed result\n */\n private processResultWithChunks = async <Out>(\n rawResult: AdapterExecutionResult,\n chunkProcessing?: ChunkProcessingConfig\n ): Promise<Out> => {\n // If chunk processing is enabled and result is a Response with readable stream\n if (\n chunkProcessing?.enabled &&\n rawResult instanceof Response &&\n hasReadableStream(rawResult)\n ) {\n // Clone the response to avoid consuming the original stream\n const clonedResponse = rawResult.clone();\n\n // Process the stream\n const processed = await processResponseStream(clonedResponse, {\n ...chunkProcessing,\n });\n\n // If accumulation is enabled, return the accumulated data\n // Otherwise, return the original response (chunks were processed via handler)\n if (chunkProcessing.accumulate && processed !== rawResult) {\n return processed as unknown as Out;\n }\n\n // Return original response if chunks were only processed via handler\n return this.adapter.getResult(rawResult) as unknown as Out;\n }\n\n // No chunk processing, return result normally\n return this.adapter.getResult(rawResult) as unknown as Out;\n };\n\n /**\n * Calculates the delay before the next retry attempt.\n *\n * @param attempt - The current attempt number (1-indexed for retries)\n * @param error - The error that occurred\n * @param retryConfig - The retry configuration\n * @returns The delay in milliseconds\n */\n private calculateRetryDelay(\n attempt: number,\n error: Error,\n retryConfig: RetryConfig\n ): number {\n const baseDelay = retryConfig.retryDelay ?? 1000;\n const maxDelay = retryConfig.maxDelay;\n\n let delay: number;\n\n if (typeof baseDelay === \"function\") {\n // Custom delay function\n delay = baseDelay(attempt, error);\n } else if (retryConfig.exponentialBackoff) {\n // Exponential backoff: delay * 2^attempt\n delay = baseDelay * Math.pow(2, attempt - 1);\n // Apply maxDelay cap if provided\n if (maxDelay !== undefined && delay > maxDelay) {\n delay = maxDelay;\n }\n } else {\n // Fixed delay\n delay = baseDelay;\n }\n\n return Math.max(0, delay);\n }\n\n /**\n * Sleeps for the specified number of milliseconds.\n *\n * @param ms - Milliseconds to sleep\n * @returns A promise that resolves after the delay\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/**\n * Type guard to check if a stage is a PipelineRequestStage.\n *\n * @template Out - The output type\n * @template AdapterExecutionResult - The adapter execution result type\n * @template RequestConfig - The request configuration type\n * @param stage - The stage to check\n * @returns True if the stage is a PipelineRequestStage\n */\nfunction isPipelineRequestStage<\n Out,\n AdapterExecutionResult,\n RequestConfig extends IRequestConfig = IRequestConfig,\n>(\n stage:\n | PipelineRequestStage<AdapterExecutionResult, Out, RequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, RequestConfig>\n): stage is PipelineRequestStage<AdapterExecutionResult, Out, RequestConfig> {\n return \"config\" in stage && !(\"request\" in stage);\n}\n\n/**\n * Type guard to check if a stage is a PipelineManagerStage.\n *\n * @template Out - The output type\n * @template AdapterExecutionResult - The adapter execution result type\n * @template RequestConfig - The request configuration type\n * @param stage - The stage to check\n * @returns True if the stage is a PipelineManagerStage\n */\nfunction isPipelineManagerStage<\n Out,\n AdapterExecutionResult,\n RequestConfig extends IRequestConfig = IRequestConfig,\n>(\n stage:\n | PipelineRequestStage<AdapterExecutionResult, Out, RequestConfig>\n | PipelineManagerStage<Out, AdapterExecutionResult, RequestConfig>\n): stage is PipelineManagerStage<Out, AdapterExecutionResult, RequestConfig> {\n return \"request\" in stage && !(\"config\" in stage);\n}\n\n/**\n * Creates a new RequestBatch with stages and adapter.\n * This is a convenience function that wraps RequestBatch.batch().\n *\n * Supports both homogeneous batches (all requests return the same type) and\n * heterogeneous batches (each request can return a different type, using tuple types).\n *\n * @template Stages - The tuple of stages (inferred from the stages parameter)\n * @template AdapterExecutionResult - The type of result returned by the adapter\n * @template RequestConfig - The type of request configuration\n * @param stages - Array or tuple of pipeline stages (request or manager stages)\n * @param adapter - The request adapter to use for HTTP requests\n * @returns A new RequestBatch instance with the stages and adapter configured\n *\n * @example\n * ```typescript\n * import { batch } from '@flow-conductor/core';\n * import { FetchRequestAdapter } from '@flow-conductor/adapter-fetch';\n *\n * const adapter = new FetchRequestAdapter();\n *\n * // Homogeneous batch - all requests return User\n * const batchInstance = batch(\n * [\n * { config: { url: '/api/users/1', method: 'GET' } },\n * { config: { url: '/api/users/2', method: 'GET' } },\n * { config: { url: '/api/users/3', method: 'GET' } }\n * ],\n * adapter\n * );\n * const results: User[] = await batchInstance.execute();\n *\n * // Heterogeneous batch - each request returns a different type\n * const heterogeneousBatch = batch(\n * [\n * { config: { url: '/api/users/1', method: 'GET' }, mapper: (r) => r.json() as Promise<User> },\n * { config: { url: '/api/products/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Product> },\n * { config: { url: '/api/orders/1', method: 'GET' }, mapper: (r) => r.json() as Promise<Order> }\n * ],\n * adapter\n * );\n * const results: [User, Product, Order] = await heterogeneousBatch.execute();\n * ```\n */\nexport function batch<\n Stages extends readonly (\n | PipelineRequestStage<AdapterExecutionResult, any, RequestConfig>\n | PipelineManagerStage<any, AdapterExecutionResult, RequestConfig>\n )[],\n AdapterExecutionResult,\n RequestConfig extends IRequestConfig = IRequestConfig,\n>(\n stages: Stages,\n adapter: RequestAdapter<AdapterExecutionResult, RequestConfig>\n): RequestBatch<StagesToTuple<Stages>, AdapterExecutionResult, RequestConfig> {\n return RequestBatch.batch(stages, adapter);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flow-conductor/core",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Core types and base classes for flow-conductor request adapters",
6
6
  "main": "./build/index.js",