From 88f6cec9b2efa3a80ee995aea5f0f85c42ac2d57 Mon Sep 17 00:00:00 2001 From: robonen Date: Fri, 9 May 2025 13:38:29 +0700 Subject: [PATCH] ci: add registry-url --- .github/workflows/npm-publish.yaml | 1 + packages/stdlib/src/async/retry/index.ts | 38 ++++++++++++ packages/stdlib/src/utils/index.ts | 8 +-- .../src/composables/useAsyncState/index.ts | 59 +++++++++++++++++++ 4 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 packages/stdlib/src/async/retry/index.ts create mode 100644 packages/vue/src/composables/useAsyncState/index.ts diff --git a/.github/workflows/npm-publish.yaml b/.github/workflows/npm-publish.yaml index 35e1e78..8a662aa 100644 --- a/.github/workflows/npm-publish.yaml +++ b/.github/workflows/npm-publish.yaml @@ -26,6 +26,7 @@ jobs: with: node-version: ${{ env.NODE_VERSION }} cache: pnpm + registry-url: 'https://registry.npmjs.org' - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/packages/stdlib/src/async/retry/index.ts b/packages/stdlib/src/async/retry/index.ts new file mode 100644 index 0000000..f9aadf8 --- /dev/null +++ b/packages/stdlib/src/async/retry/index.ts @@ -0,0 +1,38 @@ +export interface RetryOptions { + times?: number; + delay?: number; + backoff: (options: RetryOptions & { count: number }) => number; +} + +/** + * @name retry + * @category Async + * @description Retries a function a specified number of times with a delay between each retry + * + * @param {Promise} fn - The function to retry + * @param {RetryOptions} options - The options for the retry + * @returns {Promise} - The result of the function + * + * @example + * const result = await retry(() => { + * return fetch('https://jsonplaceholder.typicode.com/todos/1') + * .then(response => response.json()) + * }); + * + * @example + * const result = await retry(() => { + * return fetch('https://jsonplaceholder.typicode.com/todos/1') + * .then(response => response.json()) + * }, { times: 3, delay: 1000 }); + * + */ +export async function retry( + fn: () => Promise, + options: RetryOptions +) { + const { + times = 3, + } = options; + + let count = 0; +} diff --git a/packages/stdlib/src/utils/index.ts b/packages/stdlib/src/utils/index.ts index 2befe22..4741529 100644 --- a/packages/stdlib/src/utils/index.ts +++ b/packages/stdlib/src/utils/index.ts @@ -2,9 +2,9 @@ * @name timestamp * @category Utils * @description Returns the current timestamp - * + * * @returns {number} The current timestamp - * + * * @since 0.0.2 */ export const timestamp = () => Date.now(); @@ -13,9 +13,9 @@ export const timestamp = () => Date.now(); * @name noop * @category Utils * @description A function that does nothing - * + * * @returns {void} Nothing - * + * * @since 0.0.2 */ export const noop = () => {}; diff --git a/packages/vue/src/composables/useAsyncState/index.ts b/packages/vue/src/composables/useAsyncState/index.ts new file mode 100644 index 0000000..0091a61 --- /dev/null +++ b/packages/vue/src/composables/useAsyncState/index.ts @@ -0,0 +1,59 @@ +import { ref, shallowRef } from 'vue'; +import { isFunction } from '@robonen/stdlib'; + +export enum AsyncStateStatus { + PENDING, + FULFILLED, + REJECTED, +} + +export interface UseAsyncStateOptions { + shallow?: Shallow; + immediate?: boolean; + resetOnExecute?: boolean; + throwError?: boolean; + onError?: (error: unknown) => void; + onSuccess?: (data: Data) => void; +} + +/** + * @name useAsyncState + * @category State + * @description A composable that provides a state for async operations without setup blocking + */ +export function useAsyncState( + maybePromise: Promise | ((...args: Params) => Promise), + initialState: Data, + options?: UseAsyncStateOptions, +) { + const state = options?.shallow ? shallowRef(initialState) : ref(initialState); + const status = ref(null); + + const execute = async (...params: any[]) => { + if (options?.resetOnExecute) + state.value = initialState; + + status.value = AsyncStateStatus.PENDING; + + const promise = isFunction(maybePromise) ? maybePromise(...params as Params) : maybePromise; + + try { + const data = await promise; + state.value = data; + status.value = AsyncStateStatus.FULFILLED; + options?.onSuccess?.(data); + } + catch (error) { + status.value = AsyncStateStatus.REJECTED; + options?.onError?.(error); + + if (options?.throwError) + throw error; + } + + return state.value as Data; + }; + + if (options?.immediate) + execute(); +}