{"version":3,"file":"urql-exchange-retry.min.mjs","sources":["../src/retryExchange.ts"],"sourcesContent":["import {\n makeSubject,\n share,\n pipe,\n merge,\n filter,\n fromValue,\n delay,\n mergeMap,\n takeUntil,\n} from 'wonka';\nimport {\n makeOperation,\n Exchange,\n Operation,\n CombinedError,\n OperationResult,\n} from '@urql/core';\nimport { sourceT } from 'wonka/dist/types/src/Wonka_types.gen';\n\nexport interface RetryExchangeOptions {\n initialDelayMs?: number;\n maxDelayMs?: number;\n randomDelay?: boolean;\n maxNumberAttempts?: number;\n /** Conditionally determine whether an error should be retried */\n retryIf?: (error: CombinedError, operation: Operation) => boolean;\n /** Conditionally update operations as they're retried (retryIf can be replaced with this) */\n retryWith?: (\n error: CombinedError,\n operation: Operation\n ) => Operation | null | undefined;\n}\n\nexport const retryExchange = ({\n initialDelayMs,\n maxDelayMs,\n randomDelay,\n maxNumberAttempts,\n retryIf,\n retryWith,\n}: RetryExchangeOptions): Exchange => {\n const MIN_DELAY = initialDelayMs || 1000;\n const MAX_DELAY = maxDelayMs || 15000;\n const MAX_ATTEMPTS = maxNumberAttempts || 2;\n const RANDOM_DELAY = randomDelay || true;\n\n return ({ forward, dispatchDebug }) => ops$ => {\n const sharedOps$ = pipe(ops$, share);\n const {\n source: retry$,\n next: nextRetryOperation,\n } = makeSubject();\n\n const retryWithBackoff$ = pipe(\n retry$,\n mergeMap((op: Operation) => {\n const { key, context } = op;\n const retryCount = (context.retryCount || 0) + 1;\n let delayAmount = context.retryDelay || MIN_DELAY;\n\n const backoffFactor = Math.random() + 1.5;\n // if randomDelay is enabled and it won't exceed the max delay, apply a random\n // amount to the delay to avoid thundering herd problem\n if (RANDOM_DELAY && delayAmount * backoffFactor < MAX_DELAY) {\n delayAmount *= backoffFactor;\n }\n\n // We stop the retries if a teardown event for this operation comes in\n // But if this event comes through regularly we also stop the retries, since it's\n // basically the query retrying itself, no backoff should be added!\n const teardown$ = pipe(\n sharedOps$,\n filter(op => {\n return (\n (op.kind === 'query' || op.kind === 'teardown') && op.key === key\n );\n })\n );\n\n dispatchDebug({\n type: 'retryAttempt',\n message: `The operation has failed and a retry has been triggered (${retryCount} / ${MAX_ATTEMPTS})`,\n operation: op,\n data: {\n retryCount,\n },\n });\n\n // Add new retryDelay and retryCount to operation\n return pipe(\n fromValue(\n makeOperation(op.kind, op, {\n ...op.context,\n retryDelay: delayAmount,\n retryCount,\n })\n ),\n delay(delayAmount),\n // Stop retry if a teardown comes in\n takeUntil(teardown$)\n );\n })\n );\n\n const result$ = pipe(\n merge([sharedOps$, retryWithBackoff$]),\n forward,\n share,\n filter(res => {\n // Only retry if the error passes the conditional retryIf function (if passed)\n // or if the error contains a networkError\n if (\n !res.error ||\n (retryIf\n ? !retryIf(res.error, res.operation)\n : !retryWith && !res.error.networkError)\n ) {\n return true;\n }\n\n const maxNumberAttemptsExceeded =\n (res.operation.context.retryCount || 0) >= MAX_ATTEMPTS - 1;\n\n if (!maxNumberAttemptsExceeded) {\n const operation = retryWith\n ? retryWith(res.error, res.operation)\n : res.operation;\n if (!operation) return true;\n\n // Send failed responses to be retried by calling next on the retry$ subject\n // Exclude operations that have been retried more than the specified max\n nextRetryOperation(operation);\n return false;\n }\n\n dispatchDebug({\n type: 'retryExhausted',\n message:\n 'Maximum number of retries has been reached. No further retries will be performed.',\n operation: res.operation,\n });\n\n return true;\n })\n ) as sourceT;\n\n return result$;\n };\n};\n"],"names":["retryExchange","MIN_DELAY","MAX_DELAY","MAX_ATTEMPTS","RANDOM_DELAY","ops$","sharedOps$","share","makeSubject","retryWithBackoff$","mergeMap","op","retryCount","context","delayAmount","retryDelay","backoffFactor","Math","random","teardown$","filter","kind","key","takeUntil","delay","fromValue","makeOperation","_extends","retry$","res","error","retryIf","operation","retryWith","networkError","nextRetryOperation","forward","merge"],"mappings":"gYAkCaA,mCAQLC,oBAA8B,IAC9BC,gBAA0B,KAC1BC,uBAAoC,EACpCC,kBAA8B,qDAEGC,OAC/BC,EAAwBC,EAANF,KAIpBG,wBAEEC,EAEJC,YAAUC,2BAEFC,GAAcC,EAAQD,YAAc,GAAK,EAC3CE,EAAcD,EAAQE,YAAcd,EAElCe,EAAgBC,KAAKC,SAAW,IAGlCd,GAAgBU,EAAcE,EAAgBd,IAChDY,GAAeE,OAMXG,EAEJC,YAAOT,UAEU,UAAZA,EAAGU,MAAgC,aAAZV,EAAGU,OAAwBV,EAAGW,MAAQA,IAFlEF,CADAd,UA4BAiB,EAAUJ,GAFVK,EAAMV,EAANU,CAPAC,EACEC,EAAcf,EAAGU,KAAMV,EAAIgB,KACtBhB,EAAGE,SACNE,WAAYD,aACZF,UAvCRF,CADAkB,UAsDAR,YAAOS,OAIFA,EAAIC,SACJC,EACIA,EAAQF,EAAIC,MAAOD,EAAIG,WACvBC,GAAcJ,EAAIC,MAAMI,qBAEtB,QAINL,EAAIG,UAAUnB,QAAQD,YAAc,IAAMT,EAAe,GAE5B,KACxB6B,EAAYC,EACdA,EAAUJ,EAAIC,MAAOD,EAAIG,WACzBH,EAAIG,iBACHA,IAILG,EAAmBH,IACZ,UAUF,IAlCTZ,CADAb,EADA6B,EADAC,EAAM,CAAC/B,EAAYG"}