From 5189b446f4b8d62f7a317a951628415f8a577242 Mon Sep 17 00:00:00 2001 From: George He <georgewho96@gmail.com> Date: Mon, 17 Mar 2025 14:50:38 -0700 Subject: [PATCH] fix: add retry handling logic to parser reader and fix lint issues (#1757) Co-authored-by: Alex Yang <himself65@outlook.com> --- .changeset/thirty-hats-act.md | 8 + eslint.config.mjs | 3 + package.json | 3 + packages/cloud/src/reader.ts | 321 +++++++++++------- .../src/llm/bedrock/amazon/provider.ts | 3 +- .../community/src/llm/bedrock/amazon/utils.ts | 2 +- packages/core/src/global/constants.ts | 1 + packages/readers/src/cosmosdb.ts | 2 +- pnpm-lock.yaml | 63 +++- 9 files changed, 269 insertions(+), 137 deletions(-) create mode 100644 .changeset/thirty-hats-act.md diff --git a/.changeset/thirty-hats-act.md b/.changeset/thirty-hats-act.md new file mode 100644 index 000000000..a14122676 --- /dev/null +++ b/.changeset/thirty-hats-act.md @@ -0,0 +1,8 @@ +--- +"@llamaindex/cloud": patch +"@llamaindex/community": patch +"@llamaindex/core": patch +"@llamaindex/readers": patch +--- + +fix: add retry handling logic to parser reader and fix lint issues diff --git a/eslint.config.mjs b/eslint.config.mjs index 0e48c33f1..e083d8d75 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -50,6 +50,9 @@ export default tseslint.config( "**/lib/*", "**/deps/**", "**/.next/**", + "**/.source/**", // Ignore .source directories + "!.git", // Don't ignore .git directory + "**/.*", // Ignore all dot files and directories "**/node_modules/**", "**/build/**", "**/.docusaurus/**", diff --git a/package.json b/package.json index aac556342..4829195f5 100644 --- a/package.json +++ b/package.json @@ -46,5 +46,8 @@ "*.{json,md,yml}": [ "prettier --write" ] + }, + "dependencies": { + "p-retry": "^6.2.1" } } diff --git a/packages/cloud/src/reader.ts b/packages/cloud/src/reader.ts index 5a7af156a..529f74853 100644 --- a/packages/cloud/src/reader.ts +++ b/packages/cloud/src/reader.ts @@ -1,6 +1,8 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { type Client, createClient, createConfig } from "@hey-api/client-fetch"; import { Document, FileReader } from "@llamaindex/core/schema"; import { fs, getEnv, path } from "@llamaindex/env"; +import pRetry from "p-retry"; import { type Body_upload_file_api_v1_parsing_upload_post, type ParserLanguages, @@ -15,16 +17,18 @@ import { import { sleep } from "./utils"; export type Language = ParserLanguages; - export type ResultType = "text" | "markdown" | "json"; -//todo: should move into @llamaindex/env +// Export the backoff pattern type. +export type BackoffPattern = "constant" | "linear" | "exponential"; + +// TODO: should move into @llamaindex/env type WriteStream = { write: (text: string) => void; }; // Do not modify this variable or cause type errors -// eslint-disable-next-line @typescript-eslint/no-explicit-any, no-var +// eslint-disable-next-line no-var var process: any; /** @@ -41,48 +45,57 @@ export class LlamaParseReader extends FileReader { // The result type for the parser. resultType: ResultType = "text"; // The interval in seconds to check if the parsing is done. - checkInterval = 1; + checkInterval: number = 1; // The maximum timeout in seconds to wait for the parsing to finish. maxTimeout = 2000; // Whether to print the progress of the parsing. verbose = true; - // The language of the text to parse. + // The language to parse the file in. language: ParserLanguages[] = ["en"]; + + // New polling options: + // Controls the backoff mode: "constant", "linear", or "exponential". + backoffPattern: BackoffPattern = "linear"; + // Maximum interval in seconds between polls. + maxCheckInterval: number = 5; + // Maximum number of retryable errors before giving up. + maxErrorCount: number = 4; + // The parsing instruction for the parser. Backend default is an empty string. parsingInstruction?: string | undefined; - // Wether to ignore diagonal text (when the text rotation in degrees is not 0, 90, 180 or 270, so not a horizontal or vertical text). Backend default is false. + // Whether to ignore diagonal text (when the text rotation in degrees is not 0, 90, 180, or 270). Backend default is false. skipDiagonalText?: boolean | undefined; - // Wheter to ignore the cache and re-process the document. All documents are kept in cache for 48hours after the job was completed to avoid processing the same document twice. Backend default is false. + // Whether to ignore the cache and re-process the document. Documents are cached for 48 hours after job completion. Backend default is false. invalidateCache?: boolean | undefined; - // Wether the document should not be cached in the first place. Backend default is false. + // Whether the document should not be cached. Backend default is false. doNotCache?: boolean | undefined; - // Wether to use a faster mode to extract text from documents. This mode will skip OCR of images, and table/heading reconstruction. Note: Non-compatible with gpt4oMode. Backend default is false. + // Whether to use a faster mode to extract text (skipping OCR and table/heading reconstruction). Not compatible with gpt4oMode. Backend default is false. fastMode?: boolean | undefined; - // Wether to keep column in the text according to document layout. Reduce reconstruction accuracy, and LLM's/embedings performances in most cases. + // Whether to keep columns in the text according to document layout. May reduce reconstruction accuracy and LLM/embedings performance. doNotUnrollColumns?: boolean | undefined; - // A templated page separator to use to split the text. If the results contain `{page_number}` (e.g. JSON mode), it will be replaced by the next page number. If not set the default separator '\\n---\\n' will be used. + // A templated page separator for splitting text. If not set, default is "\n---\n". pageSeparator?: string | undefined; - //A templated prefix to add to the beginning of each page. If the results contain `{page_number}`, it will be replaced by the page number.> + // A templated prefix to add at the beginning of each page. pagePrefix?: string | undefined; - // A templated suffix to add to the end of each page. If the results contain `{page_number}`, it will be replaced by the page number. + // A templated suffix to add at the end of each page. pageSuffix?: string | undefined; - // Deprecated. Use vendorMultimodal params. Whether to use gpt-4o to extract text from documents. + // Deprecated. Use vendorMultimodal params. Whether to use gpt-4o to extract text. gpt4oMode: boolean = false; - // Deprecated. Use vendorMultimodal params. The API key for the GPT-4o API. Optional, lowers the cost of parsing. Can be set as an env variable: LLAMA_CLOUD_GPT4O_API_KEY. + // Deprecated. Use vendorMultimodal params. The API key for GPT-4o. Can be set via LLAMA_CLOUD_GPT4O_API_KEY. gpt4oApiKey?: string | undefined; - // The bounding box to use to extract text from documents. Describe as a string containing the bounding box margins. + // The bounding box margins as a string. boundingBox?: string | undefined; - // The target pages to extract text from documents. Describe as a comma separated list of page numbers. The first page of the document is page 0 + // The target pages (comma separated list, starting at 0). targetPages?: string | undefined; - // Whether or not to ignore and skip errors raised during parsing. + // Whether to ignore errors during parsing. ignoreErrors: boolean = true; - // Whether to split by page using the pageSeparator or '\n---\n' as default. + // Whether to split by page using the pageSeparator (or "\n---\n" as default). splitByPage: boolean = true; // Whether to use the vendor multimodal API. useVendorMultimodalModel: boolean = false; - // The model name for the vendor multimodal API + // The model name for the vendor multimodal API. vendorMultimodalModelName?: string | undefined; - // The API key for the multimodal API. Can also be set as an env variable: LLAMA_CLOUD_VENDOR_MULTIMODAL_API_KEY + // The API key for the multimodal API. Can be set via LLAMA_CLOUD_VENDOR_MULTIMODAL_API_KEY. vendorMultimodalApiKey?: string | undefined; webhookUrl?: string | undefined; @@ -173,7 +186,7 @@ export class LlamaParseReader extends FileReader { } this.apiKey = apiKey; if (this.baseUrl.endsWith("/")) { - this.baseUrl = this.baseUrl.slice(0, -"/".length); + this.baseUrl = this.baseUrl.slice(0, -1); } if (this.baseUrl.endsWith("/api/parsing")) { this.baseUrl = this.baseUrl.slice(0, -"/api/parsing".length); @@ -203,13 +216,19 @@ export class LlamaParseReader extends FileReader { ); } - // Create a job for the LlamaParse API + /** + * Creates a job for the LlamaParse API. + * + * @param data - The file data as a Uint8Array. + * @param filename - Optional filename for the file. + * @returns A Promise resolving to the job ID as a string. + */ async #createJob(data: Uint8Array, filename?: string): Promise<string> { if (this.verbose) { console.log("Started uploading the file"); } - // todo: remove Blob usage when we drop Node.js 18 support + // TODO: remove Blob usage when we drop Node.js 18 support const file: File | Blob = globalThis.File && filename ? new File([data], filename) @@ -320,87 +339,124 @@ export class LlamaParseReader extends FileReader { return response.data.id; } - // Get the result of the job + /** + * Retrieves the result of a parsing job. + * + * Uses a polling loop with retry logic. Each API call is retried + * up to maxErrorCount times if it fails with a 5XX or socket error. + * The delay between polls increases according to the specified backoffPattern ("constant", "linear", or "exponential"), + * capped by maxCheckInterval. + * + * @param jobId - The job ID. + * @param resultType - The type of result to fetch ("text", "json", or "markdown"). + * @returns A Promise resolving to the job result. + */ private async getJobResult( jobId: string, resultType: "text" | "json" | "markdown", - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise<any> { - const signal = AbortSignal.timeout(this.maxTimeout * 1000); let tries = 0; - while (true) { - await sleep(this.checkInterval * 1000); + let currentInterval = this.checkInterval; - // Check the job status. If unsuccessful response, checks if maximum timeout has been reached. If reached, throws an error - const result = await getJobApiV1ParsingJobJobIdGet({ - client: this.#client, - throwOnError: true, - path: { - job_id: jobId, - }, - query: { - project_id: this.project_id ?? null, - organization_id: this.organization_id ?? null, - }, - signal, - }); - const { data } = result; + while (true) { + await sleep(currentInterval * 1000); - const status = (data as Record<string, unknown>)["status"]; - // If job has completed, return the result - if (status === "SUCCESS") { - let result; - switch (resultType) { - case "json": { - result = await getJobJsonResultApiV1ParsingJobJobIdResultJsonGet({ + // Wraps the API call in a retry + let result; + try { + result = await pRetry( + () => + getJobApiV1ParsingJobJobIdGet({ client: this.#client, throwOnError: true, - path: { - job_id: jobId, - }, + path: { job_id: jobId }, query: { project_id: this.project_id ?? null, organization_id: this.organization_id ?? null, }, - signal, - }); + signal: AbortSignal.timeout(this.maxTimeout * 1000), + }), + { + retries: this.maxErrorCount, + onFailedAttempt: (error) => { + // Retry only on 5XX or socket errors. + const status = (error.cause as any)?.response?.status; + if ( + !( + (status && status >= 500 && status < 600) || + ((error.cause as any)?.code && + ((error.cause as any).code === "ECONNRESET" || + (error.cause as any).code === "ETIMEDOUT" || + (error.cause as any).code === "ECONNREFUSED")) + ) + ) { + throw error; + } + if (this.verbose) { + console.warn( + `Attempting to get job ${jobId} result (attempt ${error.attemptNumber}) failed. Retrying...`, + ); + } + }, + }, + ); + } catch (e: any) { + throw new Error( + `Max error count reached for job ${jobId}: ${e.message}`, + ); + } + + const { data } = result; + const status = (data as Record<string, unknown>)["status"]; + + if (status === "SUCCESS") { + let resultData; + switch (resultType) { + case "json": { + resultData = + await getJobJsonResultApiV1ParsingJobJobIdResultJsonGet({ + client: this.#client, + throwOnError: true, + path: { job_id: jobId }, + query: { + project_id: this.project_id ?? null, + organization_id: this.organization_id ?? null, + }, + signal: AbortSignal.timeout(this.maxTimeout * 1000), + }); break; } case "markdown": { - result = await getJobResultApiV1ParsingJobJobIdResultMarkdownGet({ - client: this.#client, - throwOnError: true, - path: { - job_id: jobId, - }, - query: { - project_id: this.project_id ?? null, - organization_id: this.organization_id ?? null, - }, - signal, - }); + resultData = + await getJobResultApiV1ParsingJobJobIdResultMarkdownGet({ + client: this.#client, + throwOnError: true, + path: { job_id: jobId }, + query: { + project_id: this.project_id ?? null, + organization_id: this.organization_id ?? null, + }, + signal: AbortSignal.timeout(this.maxTimeout * 1000), + }); break; } case "text": { - result = await getJobTextResultApiV1ParsingJobJobIdResultTextGet({ - client: this.#client, - throwOnError: true, - path: { - job_id: jobId, - }, - query: { - project_id: this.project_id ?? null, - organization_id: this.organization_id ?? null, - }, - signal, - }); + resultData = + await getJobTextResultApiV1ParsingJobJobIdResultTextGet({ + client: this.#client, + throwOnError: true, + path: { job_id: jobId }, + query: { + project_id: this.project_id ?? null, + organization_id: this.organization_id ?? null, + }, + signal: AbortSignal.timeout(this.maxTimeout * 1000), + }); break; } } - return result.data; - // If job is still pending, check if maximum timeout has been reached. If reached, throws an error + return resultData.data; } else if (status === "PENDING") { - signal.throwIfAborted(); if (this.verbose && tries % 10 === 0) { this.stdout?.write("."); } @@ -408,23 +464,35 @@ export class LlamaParseReader extends FileReader { } else { if (this.verbose) { console.error( - `Recieved Error response ${status} for job ${jobId}. Got Error Code: ${data.error_code} and Error Message: ${data.error_message}`, + `Received error response ${status} for job ${jobId}. Got Error Code: ${data.error_code} and Error Message: ${data.error_message}`, ); } throw new Error( `Failed to parse the file: ${jobId}, status: ${status}`, ); } + + // Adjust the polling interval based on the backoff pattern. + if (this.backoffPattern === "exponential") { + currentInterval = Math.min(currentInterval * 2, this.maxCheckInterval); + } else if (this.backoffPattern === "linear") { + currentInterval = Math.min( + currentInterval + this.checkInterval, + this.maxCheckInterval, + ); + } else if (this.backoffPattern === "constant") { + currentInterval = this.checkInterval; + } } } /** * Loads data from a file and returns an array of Document objects. - * To be used with resultType = "text" and "markdown" + * To be used with resultType "text" or "markdown". * - * @param {Uint8Array} fileContent - The content of the file to be loaded. - * @param {string} filename - The name of the file to be loaded. - * @return {Promise<Document[]>} A Promise object that resolves to an array of Document objects. + * @param fileContent - The content of the file as a Uint8Array. + * @param filename - Optional filename for the file. + * @returns A Promise that resolves to an array of Document objects. */ async loadDataAsContent( fileContent: Uint8Array, @@ -436,42 +504,38 @@ export class LlamaParseReader extends FileReader { console.log(`Started parsing the file under job id ${jobId}`); } - // Return results as Document objects + // Return results as Document objects. const jobResults = await this.getJobResult(jobId, this.resultType); const resultText = jobResults[this.resultType]; - // Split the text by separator if splitByPage is true + // Split the text by separator if splitByPage is true. if (this.splitByPage) { return this.splitTextBySeparator(resultText); } - return [ - new Document({ - text: resultText, - }), - ]; + return [new Document({ text: resultText })]; }) .catch((error) => { + console.warn( + `Error while parsing the file with: ${error.message ?? error.detail}`, + ); if (this.ignoreErrors) { - console.warn( - `Error while parsing the file: ${error.message ?? error.detail}`, - ); return []; } else { throw error; } }); } + /** * Loads data from a file and returns an array of JSON objects. - * To be used with resultType = "json" + * To be used with resultType "json". * - * @param {string} filePathOrContent - The file path to the file or the content of the file as a Buffer - * @return {Promise<Record<string, any>[]>} A Promise that resolves to an array of JSON objects. + * @param filePathOrContent - The file path or the file content as a Uint8Array. + * @returns A Promise that resolves to an array of JSON objects. */ async loadJson( filePathOrContent: string | Uint8Array, - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise<Record<string, any>[]> { let jobId; const isFilePath = typeof filePathOrContent === "string"; @@ -479,7 +543,7 @@ export class LlamaParseReader extends FileReader { const data = isFilePath ? await fs.readFile(filePathOrContent) : filePathOrContent; - // Creates a job for the file + // Create a job for the file. jobId = await this.#createJob( data, isFilePath ? path.basename(filePathOrContent) : undefined, @@ -488,14 +552,14 @@ export class LlamaParseReader extends FileReader { console.log(`Started parsing the file under job id ${jobId}`); } - // Return results as an array of JSON objects (same format as Python version of the reader) + // Return results as an array of JSON objects. const resultJson = await this.getJobResult(jobId, "json"); resultJson.job_id = jobId; resultJson.file_path = isFilePath ? filePathOrContent : undefined; return [resultJson]; } catch (e) { + console.error(`Error while parsing the file under job id ${jobId}`, e); if (this.ignoreErrors) { - console.error(`Error while parsing the file under job id ${jobId}`, e); return []; } else { throw e; @@ -505,27 +569,24 @@ export class LlamaParseReader extends FileReader { /** * Downloads and saves images from a given JSON result to a specified download path. - * Currently only supports resultType = "json" + * Currently only supports resultType "json". * - * @param {Record<string, any>[]} jsonResult - The JSON result containing image information. - * @param {string} downloadPath - The path to save the downloaded images. - * @return {Promise<Record<string, any>[]>} A Promise that resolves to an array of image objects. + * @param jsonResult - The JSON result containing image information. + * @param downloadPath - The path where the downloaded images will be saved. + * @returns A Promise that resolves to an array of image objects. */ async getImages( - // eslint-disable-next-line @typescript-eslint/no-explicit-any jsonResult: Record<string, any>[], downloadPath: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise<Record<string, any>[]> { try { - // Create download directory if it doesn't exist (Actually check for write access, not existence, since fsPromises does not have a `existsSync` method) + // Create download directory if it doesn't exist (checks for write access). try { await fs.access(downloadPath); } catch { await fs.mkdir(downloadPath, { recursive: true }); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any const images: Record<string, any>[] = []; for (const result of jsonResult) { const jobId = result.job_id; @@ -541,7 +602,7 @@ export class LlamaParseReader extends FileReader { imageName, ); await this.fetchAndSaveImage(imageName, imagePath, jobId); - // Assign metadata to the image + // Assign metadata to the image. image.path = imagePath; image.job_id = jobId; image.original_pdf_path = result.file_path; @@ -561,6 +622,14 @@ export class LlamaParseReader extends FileReader { } } + /** + * Constructs the file path for an image. + * + * @param downloadPath - The base download directory. + * @param jobId - The job ID. + * @param imageName - The image name. + * @returns A Promise that resolves to the full image path. + */ private async getImagePath( downloadPath: string, jobId: string, @@ -569,6 +638,13 @@ export class LlamaParseReader extends FileReader { return path.join(downloadPath, `${jobId}-${imageName}`); } + /** + * Fetches an image from the API and saves it to the specified path. + * + * @param imageName - The name of the image. + * @param imagePath - The local path to save the image. + * @param jobId - The associated job ID. + */ private async fetchAndSaveImage( imageName: string, imagePath: string, @@ -590,18 +666,21 @@ export class LlamaParseReader extends FileReader { throw new Error(`Failed to download image: ${response.error.detail}`); } const blob = (await response.data) as Blob; - // Write the image buffer to the specified imagePath + // Write the image buffer to the specified imagePath. await fs.writeFile(imagePath, new Uint8Array(await blob.arrayBuffer())); } - // Filters out invalid values (null, undefined, empty string) of specific params. + /** + * Filters out invalid values (null, undefined, empty string) for specific parameters. + * + * @param params - The parameters object. + * @param keysToCheck - The keys to check for valid values. + * @returns A new object with filtered parameters. + */ private filterSpecificParams( - // eslint-disable-next-line @typescript-eslint/no-explicit-any params: Record<string, any>, keysToCheck: string[], - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Record<string, any> { - // eslint-disable-next-line @typescript-eslint/no-explicit-any const filteredParams: Record<string, any> = {}; for (const [key, value] of Object.entries(params)) { if (keysToCheck.includes(key)) { @@ -615,6 +694,12 @@ export class LlamaParseReader extends FileReader { return filteredParams; } + /** + * Splits text into Document objects using the page separator. + * + * @param text - The text to be split. + * @returns An array of Document objects. + */ private splitTextBySeparator(text: string): Document[] { const separator = this.pageSeparator ?? "\n---\n"; const textChunks = text.split(separator); diff --git a/packages/community/src/llm/bedrock/amazon/provider.ts b/packages/community/src/llm/bedrock/amazon/provider.ts index e4f30919b..d3a8544a8 100644 --- a/packages/community/src/llm/bedrock/amazon/provider.ts +++ b/packages/community/src/llm/bedrock/amazon/provider.ts @@ -24,6 +24,7 @@ import { } from "./utils"; export class AmazonProvider extends Provider<ConverseStreamOutput> { + // eslint-disable-next-line @typescript-eslint/no-explicit-any getResultFromResponse(response: Record<string, any>): ConverseResponse { return JSON.parse(toUtf8(response.body)); } @@ -52,7 +53,7 @@ export class AmazonProvider extends Provider<ConverseStreamOutput> { } getTextFromStreamResponse(response: ResponseStream): string { - let event: ConverseStreamOutput | undefined = + const event: ConverseStreamOutput | undefined = this.getStreamingEventResponse(response); if (!event || !event.contentBlockDelta) return ""; const delta: ContentBlockDelta | undefined = event.contentBlockDelta.delta; diff --git a/packages/community/src/llm/bedrock/amazon/utils.ts b/packages/community/src/llm/bedrock/amazon/utils.ts index 9c1fe5e0e..51833c6ed 100644 --- a/packages/community/src/llm/bedrock/amazon/utils.ts +++ b/packages/community/src/llm/bedrock/amazon/utils.ts @@ -56,7 +56,7 @@ export const mapImageContent = (imageUrl: string): ImageBlock => { mimeType as keyof typeof ACCEPTED_IMAGE_MIME_TYPE_FORMAT_MAP ], - // @ts-ignore: there's a mistake in the "@aws-sdk/client-bedrock-runtime" compared to the actual api + // @ts-expect-error: there's a mistake in the "@aws-sdk/client-bedrock-runtime" compared to the actual api source: { bytes: data }, }; }; diff --git a/packages/core/src/global/constants.ts b/packages/core/src/global/constants.ts index 442f5e684..f4d99fa6b 100644 --- a/packages/core/src/global/constants.ts +++ b/packages/core/src/global/constants.ts @@ -20,4 +20,5 @@ export const DEFAULT_NAMESPACE = "docstore"; //#region llama cloud export const DEFAULT_PROJECT_NAME = "Default"; export const DEFAULT_BASE_URL = "https://api.cloud.llamaindex.ai"; +export const DEFAULT_EU_BASE_URL = "https://api.cloud.eu.llamaindex.ai"; //#endregion diff --git a/packages/readers/src/cosmosdb.ts b/packages/readers/src/cosmosdb.ts index 6a8e4f6c4..7e0e93a5c 100644 --- a/packages/readers/src/cosmosdb.ts +++ b/packages/readers/src/cosmosdb.ts @@ -60,7 +60,7 @@ export class SimpleCosmosDBReader implements BaseReader { const metadataFields = config.metadataFields; try { - let res = await container.items.query(query).fetchAll(); + const res = await container.items.query(query).fetchAll(); const documents: Document[] = []; for (const item of res.resources) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9fa0108c1..d8216154a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + p-retry: + specifier: ^6.2.1 + version: 6.2.1 devDependencies: '@changesets/cli': specifier: ^2.27.5 @@ -5528,6 +5532,9 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/retry@0.12.2': + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + '@types/statuses@2.0.5': resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} @@ -8261,6 +8268,10 @@ packages: is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + is-network-error@1.1.0: + resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==} + engines: {node: '>=16'} + is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} @@ -9826,6 +9837,10 @@ packages: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} + p-retry@6.2.1: + resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} + engines: {node: '>=16.17'} + p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -10676,6 +10691,10 @@ packages: resolution: {integrity: sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==} engines: {node: '>=14'} + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -16604,6 +16623,8 @@ snapshots: '@types/resolve@1.20.2': {} + '@types/retry@0.12.2': {} + '@types/statuses@2.0.5': {} '@types/tough-cookie@4.0.5': {} @@ -16629,10 +16650,10 @@ snapshots: '@types/node': 22.9.0 optional: true - '@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/scope-manager': 8.24.0 '@typescript-eslint/type-utils': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/utils': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3) @@ -18550,12 +18571,12 @@ snapshots: dependencies: '@next/eslint-plugin-next': 15.1.0 '@rushstack/eslint-patch': 1.10.5 - '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/parser': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3) eslint: 9.16.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.16.0(jiti@2.4.2)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.16.0(jiti@2.4.2)) eslint-plugin-react: 7.37.2(eslint@9.16.0(jiti@2.4.2)) eslint-plugin-react-hooks: 5.1.0(eslint@9.16.0(jiti@2.4.2)) @@ -18575,7 +18596,7 @@ snapshots: eslint: 9.22.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.22.0(jiti@2.4.2)) eslint-plugin-react: 7.37.2(eslint@9.22.0(jiti@2.4.2)) eslint-plugin-react-hooks: 5.1.0(eslint@9.22.0(jiti@2.4.2)) @@ -18604,7 +18625,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.16.0(jiti@2.4.2)): + eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 @@ -18616,7 +18637,7 @@ snapshots: is-glob: 4.0.3 stable-hash: 0.0.4 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color @@ -18632,18 +18653,18 @@ snapshots: is-glob: 4.0.3 stable-hash: 0.0.4 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.16.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3) eslint: 9.16.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.16.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color @@ -18658,7 +18679,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -18669,7 +18690,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.16.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.16.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -18681,13 +18702,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -20303,6 +20324,8 @@ snapshots: is-module@1.0.0: {} + is-network-error@1.1.0: {} + is-node-process@1.2.0: {} is-number-object@1.1.1: @@ -22395,6 +22418,12 @@ snapshots: p-map@2.1.0: {} + p-retry@6.2.1: + dependencies: + '@types/retry': 0.12.2 + is-network-error: 1.1.0 + retry: 0.13.1 + p-try@2.2.0: {} pac-proxy-agent@7.1.0: @@ -23393,6 +23422,8 @@ snapshots: - encoding - supports-color + retry@0.13.1: {} + reusify@1.0.4: {} rfdc@1.4.1: {} -- GitLab