UNPKG

@pinia/colada-plugin-retry

Version:
1 lines 7.07 kB
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Pinia Colada Retry plugin.\n *\n * Adds the ability to retry failed queries.\n *\n * @module @pinia/colada-plugin-retry\n */\nimport type { PiniaColadaPluginContext } from '@pinia/colada'\n\n/**\n * Options for the Pinia Colada Retry plugin.\n */\nexport interface RetryOptions {\n /**\n * The delay between retries. Can be a duration in ms or a function that\n * receives the attempt number (starts at 0) and returns a duration in ms. By\n * default, it will wait 2^attempt * 1000 ms, but never more than 30 seconds.\n *\n * @param attempt -\n * @returns\n */\n delay?: number | ((attempt: number) => number)\n\n /**\n * The maximum number of times to retry the operation. Set to 0 to disable or\n * to Infinity to retry forever. It can also be a function that receives the\n * failure count and the error and returns if it should retry. Defaults to 3.\n * **Must be a positive number**.\n */\n retry?: number | ((failureCount: number, error: unknown) => boolean)\n}\n\nexport interface RetryEntry {\n retryCount: number\n timeoutId?: ReturnType<typeof setTimeout>\n}\n\nconst RETRY_OPTIONS_DEFAULTS = {\n delay: (attempt: number) => {\n const time = Math.min(\n 2 ** attempt * 1000,\n // never more than 30 seconds\n 30_000,\n )\n console.log(`⏲️ delaying attempt #${attempt + 1} by ${time}ms`)\n return time\n },\n retry: (count) => {\n console.log(`🔄 Retrying ${'🟨'.repeat(count + 1)}${'⬜️'.repeat(2 - count)}`)\n return count < 2\n },\n} satisfies Required<RetryOptions>\n\n/**\n * Plugin that adds the ability to retry failed queries.\n *\n * @param globalOptions - global options for the retries\n */\nexport function PiniaColadaRetry(\n globalOptions?: RetryOptions,\n): (context: PiniaColadaPluginContext) => void {\n const defaults = { ...RETRY_OPTIONS_DEFAULTS, ...globalOptions }\n\n return ({ queryCache }) => {\n const retryMap = new Map<string, RetryEntry>()\n\n let isInternalCall = false\n queryCache.$onAction(({ name, args, after, onError }) => {\n // cleanup all pending retries when data is deleted (means the data is not needed anymore)\n if (name === 'remove') {\n const [cacheEntry] = args\n const key = cacheEntry.key.join('/')\n const entry = retryMap.get(key)\n if (entry) {\n clearTimeout(entry.timeoutId)\n retryMap.delete(key)\n }\n }\n\n if (name !== 'fetch') return\n const [queryEntry] = args\n const localOptions = queryEntry.options?.retry\n\n const options = {\n ...(typeof localOptions === 'object'\n ? localOptions\n : {\n retry: localOptions,\n }),\n } satisfies RetryOptions\n\n const retry = options.retry ?? defaults.retry\n const delay = options.delay ?? defaults.delay\n // avoid setting up anything at all\n if (retry === 0) return\n\n const key = queryEntry.key.join('/')\n\n // clear any pending retry\n clearTimeout(retryMap.get(key)?.timeoutId)\n // if the user manually calls the action, reset the retry count\n if (!isInternalCall) {\n retryMap.delete(key)\n }\n\n const retryFetch = () => {\n if (queryEntry.state.value.status === 'error') {\n const error = queryEntry.state.value.error\n // ensure the entry exists\n let entry = retryMap.get(key)\n if (!entry) {\n entry = { retryCount: 0 }\n retryMap.set(key, entry)\n }\n\n const shouldRetry\n = typeof retry === 'number' ? retry > entry.retryCount : retry(entry.retryCount, error)\n\n if (shouldRetry) {\n const delayTime = typeof delay === 'function' ? delay(entry.retryCount) : delay\n entry.timeoutId = setTimeout(() => {\n if (!queryEntry.active) {\n retryMap.delete(key)\n return\n }\n // NOTE: we could add some default error handler\n isInternalCall = true\n Promise.resolve(queryCache.fetch(queryEntry)).catch(\n process.env.NODE_ENV !== 'test' ? console.error : () => {},\n )\n isInternalCall = false\n if (entry) {\n entry.retryCount++\n }\n }, delayTime)\n } else {\n // remove the entry if we are not going to retry\n retryMap.delete(key)\n }\n } else {\n // remove the entry if it worked out to reset it\n retryMap.delete(key)\n }\n }\n onError(retryFetch)\n after(retryFetch)\n })\n }\n}\n\ndeclare module '@pinia/colada' {\n // eslint-disable-next-line unused-imports/no-unused-vars\n export interface UseQueryOptions<TData, TError, TDataInitial> {\n /**\n * Options for the retries of this query added by `@pinia/colada-plugin-retry`.\n */\n retry?: RetryOptions | Exclude<RetryOptions['retry'], undefined>\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCA,IAAM,yBAAyB;AAAA,EAC7B,OAAO,CAAC,YAAoB;AAC1B,UAAM,OAAO,KAAK;AAAA,MAChB,KAAK,UAAU;AAAA;AAAA,MAEf;AAAA,IACF;AACA,YAAQ,IAAI,kCAAwB,UAAU,CAAC,OAAO,IAAI,IAAI;AAC9D,WAAO;AAAA,EACT;AAAA,EACA,OAAO,CAAC,UAAU;AAChB,YAAQ,IAAI,sBAAe,YAAK,OAAO,QAAQ,CAAC,CAAC,GAAG,eAAK,OAAO,IAAI,KAAK,CAAC,EAAE;AAC5E,WAAO,QAAQ;AAAA,EACjB;AACF;AAOO,SAAS,iBACd,eAC6C;AAC7C,QAAM,WAAW,EAAE,GAAG,wBAAwB,GAAG,cAAc;AAE/D,SAAO,CAAC,EAAE,WAAW,MAAM;AACzB,UAAM,WAAW,oBAAI,IAAwB;AAE7C,QAAI,iBAAiB;AACrB,eAAW,UAAU,CAAC,EAAE,MAAM,MAAM,OAAO,QAAQ,MAAM;AAEvD,UAAI,SAAS,UAAU;AACrB,cAAM,CAAC,UAAU,IAAI;AACrB,cAAMA,OAAM,WAAW,IAAI,KAAK,GAAG;AACnC,cAAM,QAAQ,SAAS,IAAIA,IAAG;AAC9B,YAAI,OAAO;AACT,uBAAa,MAAM,SAAS;AAC5B,mBAAS,OAAOA,IAAG;AAAA,QACrB;AAAA,MACF;AAEA,UAAI,SAAS,QAAS;AACtB,YAAM,CAAC,UAAU,IAAI;AACrB,YAAM,eAAe,WAAW,SAAS;AAEzC,YAAM,UAAU;AAAA,QACd,GAAI,OAAO,iBAAiB,WACxB,eACA;AAAA,UACE,OAAO;AAAA,QACT;AAAA,MACN;AAEA,YAAM,QAAQ,QAAQ,SAAS,SAAS;AACxC,YAAM,QAAQ,QAAQ,SAAS,SAAS;AAExC,UAAI,UAAU,EAAG;AAEjB,YAAM,MAAM,WAAW,IAAI,KAAK,GAAG;AAGnC,mBAAa,SAAS,IAAI,GAAG,GAAG,SAAS;AAEzC,UAAI,CAAC,gBAAgB;AACnB,iBAAS,OAAO,GAAG;AAAA,MACrB;AAEA,YAAM,aAAa,MAAM;AACvB,YAAI,WAAW,MAAM,MAAM,WAAW,SAAS;AAC7C,gBAAM,QAAQ,WAAW,MAAM,MAAM;AAErC,cAAI,QAAQ,SAAS,IAAI,GAAG;AAC5B,cAAI,CAAC,OAAO;AACV,oBAAQ,EAAE,YAAY,EAAE;AACxB,qBAAS,IAAI,KAAK,KAAK;AAAA,UACzB;AAEA,gBAAM,cACF,OAAO,UAAU,WAAW,QAAQ,MAAM,aAAa,MAAM,MAAM,YAAY,KAAK;AAExF,cAAI,aAAa;AACf,kBAAM,YAAY,OAAO,UAAU,aAAa,MAAM,MAAM,UAAU,IAAI;AAC1E,kBAAM,YAAY,WAAW,MAAM;AACjC,kBAAI,CAAC,WAAW,QAAQ;AACtB,yBAAS,OAAO,GAAG;AACnB;AAAA,cACF;AAEA,+BAAiB;AACjB,sBAAQ,QAAQ,WAAW,MAAM,UAAU,CAAC,EAAE;AAAA,gBAC5C,QAAQ,IAAI,aAAa,SAAS,QAAQ,QAAQ,MAAM;AAAA,gBAAC;AAAA,cAC3D;AACA,+BAAiB;AACjB,kBAAI,OAAO;AACT,sBAAM;AAAA,cACR;AAAA,YACF,GAAG,SAAS;AAAA,UACd,OAAO;AAEL,qBAAS,OAAO,GAAG;AAAA,UACrB;AAAA,QACF,OAAO;AAEL,mBAAS,OAAO,GAAG;AAAA,QACrB;AAAA,MACF;AACA,cAAQ,UAAU;AAClB,YAAM,UAAU;AAAA,IAClB,CAAC;AAAA,EACH;AACF;","names":["key"]}