@pinia/colada-plugin-retry 0.2.0 → 0.2.2

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/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
+ import { ShallowRef } from "vue";
1
2
  import { PiniaColadaPluginContext } from "@pinia/colada";
2
3
 
3
4
  //#region src/retry.d.ts
4
-
5
5
  /**
6
6
  * Options for the Pinia Colada Retry plugin.
7
7
  */
@@ -40,6 +40,22 @@ declare module '@pinia/colada' {
40
40
  */
41
41
  retry?: RetryOptions | Exclude<RetryOptions['retry'], undefined>;
42
42
  }
43
+ interface UseQueryEntryExtensions<TData, TError, TDataInitial> {
44
+ /**
45
+ * Whether the query is currently retrying. Requires the `@pinia/colada-plugin-retry` plugin.
46
+ */
47
+ isRetrying: ShallowRef<boolean>;
48
+ /**
49
+ * The number of retries that have been scheduled so far. Resets on success or manual refetch.
50
+ * Requires the `@pinia/colada-plugin-retry` plugin.
51
+ */
52
+ retryCount: ShallowRef<number>;
53
+ /**
54
+ * The error that triggered the current retry. `null` when not retrying or when retries are exhausted.
55
+ * Requires the `@pinia/colada-plugin-retry` plugin.
56
+ */
57
+ retryError: ShallowRef<TError | null>;
58
+ }
43
59
  }
44
60
  //#endregion
45
61
  export { PiniaColadaRetry, RetryEntry, RetryOptions };
package/dist/index.mjs CHANGED
@@ -1,12 +1,14 @@
1
+ import { shallowRef, toValue } from "vue";
2
+
1
3
  //#region src/retry.ts
2
4
  const RETRY_OPTIONS_DEFAULTS = {
3
5
  delay: (attempt) => {
4
6
  const time = Math.min(2 ** attempt * 1e3, 3e4);
5
- console.log(`⏲️ delaying attempt #${attempt + 1} by ${time}ms`);
7
+ if (process.env.NODE_ENV === "development") console.log(`⏲️ delaying attempt #${attempt + 1} by ${time}ms`);
6
8
  return time;
7
9
  },
8
10
  retry: (count) => {
9
- console.log(`🔄 Retrying ${"🟨".repeat(count + 1)}${"⬜️".repeat(2 - count)}`);
11
+ if (process.env.NODE_ENV === "development") console.log(`🔄 Retrying ${"🟨".repeat(count + 1)}${"⬜️".repeat(2 - count)}`);
10
12
  return count < 2;
11
13
  }
12
14
  };
@@ -20,17 +22,26 @@ function PiniaColadaRetry(globalOptions) {
20
22
  ...RETRY_OPTIONS_DEFAULTS,
21
23
  ...globalOptions
22
24
  };
23
- return ({ queryCache }) => {
25
+ return ({ queryCache, scope }) => {
24
26
  const retryMap = /* @__PURE__ */ new Map();
25
27
  let isInternalCall = false;
26
28
  queryCache.$onAction(({ name, args, after, onError }) => {
29
+ if (name === "extend") {
30
+ const [entry] = args;
31
+ scope.run(() => {
32
+ entry.ext.isRetrying = shallowRef(false);
33
+ entry.ext.retryCount = shallowRef(0);
34
+ entry.ext.retryError = shallowRef(null);
35
+ });
36
+ return;
37
+ }
27
38
  if (name === "remove") {
28
39
  const [cacheEntry] = args;
29
- const key$1 = cacheEntry.key.join("/");
30
- const entry = retryMap.get(key$1);
40
+ const key = cacheEntry.key.join("/");
41
+ const entry = retryMap.get(key);
31
42
  if (entry) {
32
43
  clearTimeout(entry.timeoutId);
33
- retryMap.delete(key$1);
44
+ retryMap.delete(key);
34
45
  }
35
46
  }
36
47
  if (name !== "fetch") return;
@@ -42,7 +53,13 @@ function PiniaColadaRetry(globalOptions) {
42
53
  if (retry === 0) return;
43
54
  const key = queryEntry.key.join("/");
44
55
  clearTimeout(retryMap.get(key)?.timeoutId);
45
- if (!isInternalCall) retryMap.delete(key);
56
+ if (!isInternalCall) {
57
+ retryMap.delete(key);
58
+ queryEntry.ext.isRetrying.value = false;
59
+ queryEntry.ext.retryCount.value = 0;
60
+ queryEntry.ext.retryError.value = null;
61
+ }
62
+ const previousState = queryEntry.state.value;
46
63
  const retryFetch = () => {
47
64
  if (queryEntry.state.value.status === "error") {
48
65
  const error = queryEntry.state.value.error;
@@ -52,10 +69,17 @@ function PiniaColadaRetry(globalOptions) {
52
69
  retryMap.set(key, entry);
53
70
  }
54
71
  if (typeof retry === "number" ? retry > entry.retryCount : retry(entry.retryCount, error)) {
72
+ queryEntry.ext.isRetrying.value = true;
73
+ queryEntry.ext.retryCount.value = entry.retryCount + 1;
74
+ queryEntry.ext.retryError.value = error;
75
+ queryEntry.state.value = previousState;
55
76
  const delayTime = typeof delay === "function" ? delay(entry.retryCount) : delay;
56
77
  entry.timeoutId = setTimeout(() => {
57
- if (!queryEntry.active) {
78
+ if (!queryEntry.active || toValue(queryEntry.options?.enabled) === false) {
58
79
  retryMap.delete(key);
80
+ queryEntry.ext.isRetrying.value = false;
81
+ queryEntry.ext.retryCount.value = 0;
82
+ queryEntry.ext.retryError.value = null;
59
83
  return;
60
84
  }
61
85
  isInternalCall = true;
@@ -63,8 +87,17 @@ function PiniaColadaRetry(globalOptions) {
63
87
  isInternalCall = false;
64
88
  if (entry) entry.retryCount++;
65
89
  }, delayTime);
66
- } else retryMap.delete(key);
67
- } else retryMap.delete(key);
90
+ } else {
91
+ queryEntry.ext.isRetrying.value = false;
92
+ queryEntry.ext.retryError.value = null;
93
+ retryMap.delete(key);
94
+ }
95
+ } else {
96
+ queryEntry.ext.isRetrying.value = false;
97
+ queryEntry.ext.retryCount.value = 0;
98
+ queryEntry.ext.retryError.value = null;
99
+ retryMap.delete(key);
100
+ }
68
101
  };
69
102
  onError(retryFetch);
70
103
  after(retryFetch);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["key"],"sources":["../src/retry.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 // oxlint-disable-next-line no-console\n console.log(`⏲️ delaying attempt #${attempt + 1} by ${time}ms`)\n return time\n },\n retry: (count) => {\n // oxlint-disable-next-line no-console\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":";AAqCA,MAAM,yBAAyB;CAC7B,QAAQ,YAAoB;EAC1B,MAAM,OAAO,KAAK,IAChB,KAAK,UAAU,KAEf,IACD;AAED,UAAQ,IAAI,wBAAwB,UAAU,EAAE,MAAM,KAAK,IAAI;AAC/D,SAAO;;CAET,QAAQ,UAAU;AAEhB,UAAQ,IAAI,eAAe,KAAK,OAAO,QAAQ,EAAE,GAAG,KAAK,OAAO,IAAI,MAAM,GAAG;AAC7E,SAAO,QAAQ;;CAElB;;;;;;AAOD,SAAgB,iBACd,eAC6C;CAC7C,MAAM,WAAW;EAAE,GAAG;EAAwB,GAAG;EAAe;AAEhE,SAAQ,EAAE,iBAAiB;EACzB,MAAM,2BAAW,IAAI,KAAyB;EAE9C,IAAI,iBAAiB;AACrB,aAAW,WAAW,EAAE,MAAM,MAAM,OAAO,cAAc;AAEvD,OAAI,SAAS,UAAU;IACrB,MAAM,CAAC,cAAc;IACrB,MAAMA,QAAM,WAAW,IAAI,KAAK,IAAI;IACpC,MAAM,QAAQ,SAAS,IAAIA,MAAI;AAC/B,QAAI,OAAO;AACT,kBAAa,MAAM,UAAU;AAC7B,cAAS,OAAOA,MAAI;;;AAIxB,OAAI,SAAS,QAAS;GACtB,MAAM,CAAC,cAAc;GACrB,MAAM,eAAe,WAAW,SAAS;GAEzC,MAAM,UAAU,EACd,GAAI,OAAO,iBAAiB,WACxB,eACA,EACE,OAAO,cACR,EACN;GAED,MAAM,QAAQ,QAAQ,SAAS,SAAS;GACxC,MAAM,QAAQ,QAAQ,SAAS,SAAS;AAExC,OAAI,UAAU,EAAG;GAEjB,MAAM,MAAM,WAAW,IAAI,KAAK,IAAI;AAGpC,gBAAa,SAAS,IAAI,IAAI,EAAE,UAAU;AAE1C,OAAI,CAAC,eACH,UAAS,OAAO,IAAI;GAGtB,MAAM,mBAAmB;AACvB,QAAI,WAAW,MAAM,MAAM,WAAW,SAAS;KAC7C,MAAM,QAAQ,WAAW,MAAM,MAAM;KAErC,IAAI,QAAQ,SAAS,IAAI,IAAI;AAC7B,SAAI,CAAC,OAAO;AACV,cAAQ,EAAE,YAAY,GAAG;AACzB,eAAS,IAAI,KAAK,MAAM;;AAM1B,SAFE,OAAO,UAAU,WAAW,QAAQ,MAAM,aAAa,MAAM,MAAM,YAAY,MAAM,EAEtE;MACf,MAAM,YAAY,OAAO,UAAU,aAAa,MAAM,MAAM,WAAW,GAAG;AAC1E,YAAM,YAAY,iBAAiB;AACjC,WAAI,CAAC,WAAW,QAAQ;AACtB,iBAAS,OAAO,IAAI;AACpB;;AAGF,wBAAiB;AACjB,eAAQ,QAAQ,WAAW,MAAM,WAAW,CAAC,CAAC,MAC5C,QAAQ,IAAI,aAAa,SAAS,QAAQ,cAAc,GACzD;AACD,wBAAiB;AACjB,WAAI,MACF,OAAM;SAEP,UAAU;WAGb,UAAS,OAAO,IAAI;UAItB,UAAS,OAAO,IAAI;;AAGxB,WAAQ,WAAW;AACnB,SAAM,WAAW;IACjB"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/retry.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'\nimport type { ShallowRef } from 'vue'\nimport { shallowRef, toValue } from 'vue'\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 if (process.env.NODE_ENV === 'development') {\n // oxlint-disable-next-line no-console\n console.log(`⏲️ delaying attempt #${attempt + 1} by ${time}ms`)\n }\n return time\n },\n retry: (count) => {\n if (process.env.NODE_ENV === 'development') {\n // oxlint-disable-next-line no-console\n console.log(`🔄 Retrying ${'🟨'.repeat(count + 1)}${'⬜️'.repeat(2 - count)}`)\n }\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, scope }) => {\n const retryMap = new Map<string, RetryEntry>()\n\n let isInternalCall = false\n queryCache.$onAction(({ name, args, after, onError }) => {\n if (name === 'extend') {\n const [entry] = args\n scope.run(() => {\n entry.ext.isRetrying = shallowRef(false)\n entry.ext.retryCount = shallowRef(0)\n entry.ext.retryError = shallowRef(null)\n })\n return\n }\n\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 queryEntry.ext.isRetrying.value = false\n queryEntry.ext.retryCount.value = 0\n queryEntry.ext.retryError.value = null\n }\n\n // capture state before the fetch runs so we can revert during retries\n const previousState = queryEntry.state.value\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 queryEntry.ext.isRetrying.value = true\n queryEntry.ext.retryCount.value = entry.retryCount + 1\n queryEntry.ext.retryError.value = error\n // revert to pre-fetch state so the error is only visible via retryError\n queryEntry.state.value = previousState\n const delayTime = typeof delay === 'function' ? delay(entry.retryCount) : delay\n entry.timeoutId = setTimeout(() => {\n if (!queryEntry.active || toValue(queryEntry.options?.enabled) === false) {\n retryMap.delete(key)\n queryEntry.ext.isRetrying.value = false\n queryEntry.ext.retryCount.value = 0\n queryEntry.ext.retryError.value = null\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 queryEntry.ext.isRetrying.value = false\n queryEntry.ext.retryError.value = null\n retryMap.delete(key)\n }\n } else {\n // remove the entry if it worked out to reset it\n queryEntry.ext.isRetrying.value = false\n queryEntry.ext.retryCount.value = 0\n queryEntry.ext.retryError.value = null\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 // eslint-disable-next-line unused-imports/no-unused-vars\n interface UseQueryEntryExtensions<TData, TError, TDataInitial> {\n /**\n * Whether the query is currently retrying. Requires the `@pinia/colada-plugin-retry` plugin.\n */\n isRetrying: ShallowRef<boolean>\n /**\n * The number of retries that have been scheduled so far. Resets on success or manual refetch.\n * Requires the `@pinia/colada-plugin-retry` plugin.\n */\n retryCount: ShallowRef<number>\n /**\n * The error that triggered the current retry. `null` when not retrying or when retries are exhausted.\n * Requires the `@pinia/colada-plugin-retry` plugin.\n */\n retryError: ShallowRef<TError | null>\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.2.0",
7
+ "version": "0.2.2",
8
8
  "description": "Retry failed requests with Pinia Colada",
9
9
  "author": {
10
10
  "name": "Eduardo San Martin Morote",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "license": "MIT",
14
14
  "funding": "https://github.com/sponsors/posva",
15
- "homepage": "https://github.com/posva/pinia-colada/plugins/retry#readme",
15
+ "homepage": "https://github.com/posva/pinia-colada/blob/main/plugins/retry/README.md",
16
16
  "repository": "posva/pinia-colada",
17
17
  "bugs": {
18
18
  "url": "https://github.com/posva/pinia-colada/issues"
@@ -28,18 +28,8 @@
28
28
  "layer"
29
29
  ],
30
30
  "sideEffects": false,
31
- "exports": {
32
- ".": {
33
- "types": {
34
- "import": "./dist/index.d.mts",
35
- "require": "./dist/index.d.cts"
36
- },
37
- "import": "./dist/index.mjs",
38
- "require": "./dist/index.cjs"
39
- }
40
- },
41
- "main": "./dist/index.cjs",
42
- "module": "./dist/index.mjs",
31
+ "exports": "./dist/index.mjs",
32
+ "main": "./dist/index.mjs",
43
33
  "types": "./dist/index.d.mts",
44
34
  "typesVersions": {
45
35
  "*": {
@@ -55,10 +45,10 @@
55
45
  "dist"
56
46
  ],
57
47
  "peerDependencies": {
58
- "@pinia/colada": ">=0.20.0"
48
+ "@pinia/colada": ">=0.21.5"
59
49
  },
60
50
  "devDependencies": {
61
- "@pinia/colada": "^0.20.0"
51
+ "@pinia/colada": "^0.21.5"
62
52
  },
63
53
  "scripts": {
64
54
  "build": "tsdown",
package/dist/index.cjs DELETED
@@ -1,78 +0,0 @@
1
-
2
- //#region src/retry.ts
3
- const RETRY_OPTIONS_DEFAULTS = {
4
- delay: (attempt) => {
5
- const time = Math.min(2 ** attempt * 1e3, 3e4);
6
- console.log(`⏲️ delaying attempt #${attempt + 1} by ${time}ms`);
7
- return time;
8
- },
9
- retry: (count) => {
10
- console.log(`🔄 Retrying ${"🟨".repeat(count + 1)}${"⬜️".repeat(2 - count)}`);
11
- return count < 2;
12
- }
13
- };
14
- /**
15
- * Plugin that adds the ability to retry failed queries.
16
- *
17
- * @param globalOptions - global options for the retries
18
- */
19
- function PiniaColadaRetry(globalOptions) {
20
- const defaults = {
21
- ...RETRY_OPTIONS_DEFAULTS,
22
- ...globalOptions
23
- };
24
- return ({ queryCache }) => {
25
- const retryMap = /* @__PURE__ */ new Map();
26
- let isInternalCall = false;
27
- queryCache.$onAction(({ name, args, after, onError }) => {
28
- if (name === "remove") {
29
- const [cacheEntry] = args;
30
- const key$1 = cacheEntry.key.join("/");
31
- const entry = retryMap.get(key$1);
32
- if (entry) {
33
- clearTimeout(entry.timeoutId);
34
- retryMap.delete(key$1);
35
- }
36
- }
37
- if (name !== "fetch") return;
38
- const [queryEntry] = args;
39
- const localOptions = queryEntry.options?.retry;
40
- const options = { ...typeof localOptions === "object" ? localOptions : { retry: localOptions } };
41
- const retry = options.retry ?? defaults.retry;
42
- const delay = options.delay ?? defaults.delay;
43
- if (retry === 0) return;
44
- const key = queryEntry.key.join("/");
45
- clearTimeout(retryMap.get(key)?.timeoutId);
46
- if (!isInternalCall) retryMap.delete(key);
47
- const retryFetch = () => {
48
- if (queryEntry.state.value.status === "error") {
49
- const error = queryEntry.state.value.error;
50
- let entry = retryMap.get(key);
51
- if (!entry) {
52
- entry = { retryCount: 0 };
53
- retryMap.set(key, entry);
54
- }
55
- if (typeof retry === "number" ? retry > entry.retryCount : retry(entry.retryCount, error)) {
56
- const delayTime = typeof delay === "function" ? delay(entry.retryCount) : delay;
57
- entry.timeoutId = setTimeout(() => {
58
- if (!queryEntry.active) {
59
- retryMap.delete(key);
60
- return;
61
- }
62
- isInternalCall = true;
63
- Promise.resolve(queryCache.fetch(queryEntry)).catch(process.env.NODE_ENV !== "test" ? console.error : () => {});
64
- isInternalCall = false;
65
- if (entry) entry.retryCount++;
66
- }, delayTime);
67
- } else retryMap.delete(key);
68
- } else retryMap.delete(key);
69
- };
70
- onError(retryFetch);
71
- after(retryFetch);
72
- });
73
- };
74
- }
75
-
76
- //#endregion
77
- exports.PiniaColadaRetry = PiniaColadaRetry;
78
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["key"],"sources":["../src/retry.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 // oxlint-disable-next-line no-console\n console.log(`⏲️ delaying attempt #${attempt + 1} by ${time}ms`)\n return time\n },\n retry: (count) => {\n // oxlint-disable-next-line no-console\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":";;AAqCA,MAAM,yBAAyB;CAC7B,QAAQ,YAAoB;EAC1B,MAAM,OAAO,KAAK,IAChB,KAAK,UAAU,KAEf,IACD;AAED,UAAQ,IAAI,wBAAwB,UAAU,EAAE,MAAM,KAAK,IAAI;AAC/D,SAAO;;CAET,QAAQ,UAAU;AAEhB,UAAQ,IAAI,eAAe,KAAK,OAAO,QAAQ,EAAE,GAAG,KAAK,OAAO,IAAI,MAAM,GAAG;AAC7E,SAAO,QAAQ;;CAElB;;;;;;AAOD,SAAgB,iBACd,eAC6C;CAC7C,MAAM,WAAW;EAAE,GAAG;EAAwB,GAAG;EAAe;AAEhE,SAAQ,EAAE,iBAAiB;EACzB,MAAM,2BAAW,IAAI,KAAyB;EAE9C,IAAI,iBAAiB;AACrB,aAAW,WAAW,EAAE,MAAM,MAAM,OAAO,cAAc;AAEvD,OAAI,SAAS,UAAU;IACrB,MAAM,CAAC,cAAc;IACrB,MAAMA,QAAM,WAAW,IAAI,KAAK,IAAI;IACpC,MAAM,QAAQ,SAAS,IAAIA,MAAI;AAC/B,QAAI,OAAO;AACT,kBAAa,MAAM,UAAU;AAC7B,cAAS,OAAOA,MAAI;;;AAIxB,OAAI,SAAS,QAAS;GACtB,MAAM,CAAC,cAAc;GACrB,MAAM,eAAe,WAAW,SAAS;GAEzC,MAAM,UAAU,EACd,GAAI,OAAO,iBAAiB,WACxB,eACA,EACE,OAAO,cACR,EACN;GAED,MAAM,QAAQ,QAAQ,SAAS,SAAS;GACxC,MAAM,QAAQ,QAAQ,SAAS,SAAS;AAExC,OAAI,UAAU,EAAG;GAEjB,MAAM,MAAM,WAAW,IAAI,KAAK,IAAI;AAGpC,gBAAa,SAAS,IAAI,IAAI,EAAE,UAAU;AAE1C,OAAI,CAAC,eACH,UAAS,OAAO,IAAI;GAGtB,MAAM,mBAAmB;AACvB,QAAI,WAAW,MAAM,MAAM,WAAW,SAAS;KAC7C,MAAM,QAAQ,WAAW,MAAM,MAAM;KAErC,IAAI,QAAQ,SAAS,IAAI,IAAI;AAC7B,SAAI,CAAC,OAAO;AACV,cAAQ,EAAE,YAAY,GAAG;AACzB,eAAS,IAAI,KAAK,MAAM;;AAM1B,SAFE,OAAO,UAAU,WAAW,QAAQ,MAAM,aAAa,MAAM,MAAM,YAAY,MAAM,EAEtE;MACf,MAAM,YAAY,OAAO,UAAU,aAAa,MAAM,MAAM,WAAW,GAAG;AAC1E,YAAM,YAAY,iBAAiB;AACjC,WAAI,CAAC,WAAW,QAAQ;AACtB,iBAAS,OAAO,IAAI;AACpB;;AAGF,wBAAiB;AACjB,eAAQ,QAAQ,WAAW,MAAM,WAAW,CAAC,CAAC,MAC5C,QAAQ,IAAI,aAAa,SAAS,QAAQ,cAAc,GACzD;AACD,wBAAiB;AACjB,WAAI,MACF,OAAM;SAEP,UAAU;WAGb,UAAS,OAAO,IAAI;UAItB,UAAS,OAAO,IAAI;;AAGxB,WAAQ,WAAW;AACnB,SAAM,WAAW;IACjB"}
package/dist/index.d.cts DELETED
@@ -1,46 +0,0 @@
1
- import { PiniaColadaPluginContext } from "@pinia/colada";
2
-
3
- //#region src/retry.d.ts
4
-
5
- /**
6
- * Options for the Pinia Colada Retry plugin.
7
- */
8
- interface RetryOptions {
9
- /**
10
- * The delay between retries. Can be a duration in ms or a function that
11
- * receives the attempt number (starts at 0) and returns a duration in ms. By
12
- * default, it will wait 2^attempt * 1000 ms, but never more than 30 seconds.
13
- *
14
- * @param attempt -
15
- * @returns
16
- */
17
- delay?: number | ((attempt: number) => number);
18
- /**
19
- * The maximum number of times to retry the operation. Set to 0 to disable or
20
- * to Infinity to retry forever. It can also be a function that receives the
21
- * failure count and the error and returns if it should retry. Defaults to 3.
22
- * **Must be a positive number**.
23
- */
24
- retry?: number | ((failureCount: number, error: unknown) => boolean);
25
- }
26
- interface RetryEntry {
27
- retryCount: number;
28
- timeoutId?: ReturnType<typeof setTimeout>;
29
- }
30
- /**
31
- * Plugin that adds the ability to retry failed queries.
32
- *
33
- * @param globalOptions - global options for the retries
34
- */
35
- declare function PiniaColadaRetry(globalOptions?: RetryOptions): (context: PiniaColadaPluginContext) => void;
36
- declare module '@pinia/colada' {
37
- interface UseQueryOptions<TData, TError, TDataInitial> {
38
- /**
39
- * Options for the retries of this query added by `@pinia/colada-plugin-retry`.
40
- */
41
- retry?: RetryOptions | Exclude<RetryOptions['retry'], undefined>;
42
- }
43
- }
44
- //#endregion
45
- export { PiniaColadaRetry, RetryEntry, RetryOptions };
46
- //# sourceMappingURL=index.d.cts.map