UNPKG

@invoiceddd/react

Version:

React integration utilities for InvoiceDDD system

431 lines (426 loc) 12.6 kB
import { CreateInvoiceUseCase, RequestInvoiceUseCase, GetInvoiceUseCase, ListInvoicesUseCase, InvoiceSystem } from 'invoiceddd'; import { useState, useEffect, createContext, useContext, useCallback } from 'react'; import { Effect } from 'effect'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var InvoiceSystemContext = /* @__PURE__ */ createContext(null); function InvoiceSystemProvider({ children, options = {}, autoStart = true }) { const [system, setSystem] = useState(null); const [config, setConfig] = useState(null); const [runtime, setRuntime] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let mounted = true; const initializeSystem = /* @__PURE__ */ __name(async () => { try { setIsLoading(true); setError(null); const systemInstance = await InvoiceSystem.create(options); if (!mounted) return; setSystem(systemInstance); setConfig(systemInstance.getConfig()); setRuntime(systemInstance.getRuntime()); if (autoStart && options.autoStartServer !== true) { await systemInstance.startServer(); } } catch (err) { if (!mounted) return; setError(err instanceof Error ? err : new Error(String(err))); } finally { if (mounted) { setIsLoading(false); } } }, "initializeSystem"); initializeSystem(); return () => { mounted = false; if (system) { system.stop().catch(console.error); } }; }, [ options, autoStart ]); const contextValue = { system, config, isLoading, error, runtime }; return /* @__PURE__ */ React.createElement(InvoiceSystemContext.Provider, { value: contextValue }, children); } __name(InvoiceSystemProvider, "InvoiceSystemProvider"); function useInvoiceSystem() { const context = useContext(InvoiceSystemContext); if (!context) { throw new Error("useInvoiceSystem must be used within an InvoiceSystemProvider"); } return context; } __name(useInvoiceSystem, "useInvoiceSystem"); function useCreateInvoice() { const { runtime } = useInvoiceSystem(); const [state, setState] = useState({ data: null, isLoading: false, error: null }); const createInvoice = useCallback(async (input) => { if (!runtime) { throw new Error("Invoice system not available"); } setState({ data: null, isLoading: true, error: null }); try { const createEffect = Effect.gen(function* () { const createUseCase = yield* CreateInvoiceUseCase; return yield* createUseCase.execute(input); }); const result = await runtime.runPromise(createEffect); setState({ data: result, isLoading: false, error: null }); return result; } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); setState({ data: null, isLoading: false, error: err }); return null; } }, [ runtime ]); return { createInvoice, ...state }; } __name(useCreateInvoice, "useCreateInvoice"); function useRequestInvoice() { const { runtime } = useInvoiceSystem(); const [state, setState] = useState({ data: null, isLoading: false, error: null }); const requestInvoice = useCallback(async (input) => { if (!runtime) { throw new Error("Invoice system not available"); } setState({ data: null, isLoading: true, error: null }); try { const requestEffect = Effect.gen(function* () { const requestUseCase = yield* RequestInvoiceUseCase; return yield* requestUseCase.execute(input); }); const draft = await runtime.runPromise(requestEffect); setState({ data: draft, isLoading: false, error: null }); return draft; } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); setState({ data: null, isLoading: false, error: err }); return null; } }, [ runtime ]); return { requestInvoice, ...state }; } __name(useRequestInvoice, "useRequestInvoice"); function useInvoice(input) { const { runtime } = useInvoiceSystem(); const [state, setState] = useState({ data: null, isLoading: false, error: null }); const fetchInvoice = useCallback(async () => { if (!runtime) return; setState((prev) => ({ ...prev, isLoading: true, error: null })); try { const getEffect = Effect.gen(function* () { const getUseCase = yield* GetInvoiceUseCase; return yield* getUseCase.execute(input); }); const result = await runtime.runPromise(getEffect); setState({ data: result, isLoading: false, error: null }); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); setState((prev) => ({ ...prev, isLoading: false, error: err })); } }, [ runtime, input ]); useEffect(() => { fetchInvoice(); }, [ fetchInvoice ]); return { invoice: state.data, isLoading: state.isLoading, error: state.error, refetch: fetchInvoice }; } __name(useInvoice, "useInvoice"); function useInvoices(input = {}) { const { runtime } = useInvoiceSystem(); const [state, setState] = useState({ data: null, isLoading: false, error: null }); const fetchInvoices = useCallback(async (append = false) => { if (!runtime) return; setState((prev) => ({ ...prev, isLoading: true, error: null })); try { const currentPage = append && state.data ? state.data.page + 1 : input.pagination?.page || 1; const inputWithPage = { ...input, pagination: { ...input.pagination, page: currentPage } }; const listEffect = Effect.gen(function* () { const listUseCase = yield* ListInvoicesUseCase; return yield* listUseCase.execute(inputWithPage); }); const result = await runtime.runPromise(listEffect); setState((prev) => ({ data: append && prev.data ? { ...result, invoices: [ ...prev.data.invoices, ...result.invoices ] } : result, isLoading: false, error: null })); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); setState((prev) => ({ ...prev, isLoading: false, error: err })); } }, [ runtime, input, state.data ]); const loadMore = useCallback(() => { fetchInvoices(true); }, [ fetchInvoices ]); const refetch = useCallback(() => { fetchInvoices(false); }, [ fetchInvoices ]); useEffect(() => { fetchInvoices(false); }, [ input, runtime ]); const hasMore = state.data ? state.data.hasNextPage : false; return { invoices: state.data, isLoading: state.isLoading, error: state.error, loadMore, hasMore, refetch }; } __name(useInvoices, "useInvoices"); // src/hoc.tsx function withInvoiceSystem(Component) { const WrappedComponent = /* @__PURE__ */ __name((props) => { const { runtime, isLoading, error } = useInvoiceSystem(); if (isLoading) { return /* @__PURE__ */ React.createElement("div", null, "Loading invoice system..."); } if (error) { return /* @__PURE__ */ React.createElement("div", null, "Error initializing invoice system: ", error.message); } if (!runtime) { return /* @__PURE__ */ React.createElement("div", null, "Invoice system not available"); } const runInvoiceEffect = /* @__PURE__ */ __name((effect) => { return runtime.runPromise(effect); }, "runInvoiceEffect"); const enhancedProps = { ...props, invoiceRuntime: runtime, runInvoiceEffect }; return /* @__PURE__ */ React.createElement(Component, enhancedProps); }, "WrappedComponent"); WrappedComponent.displayName = `withInvoiceSystem(${Component.displayName || Component.name})`; return WrappedComponent; } __name(withInvoiceSystem, "withInvoiceSystem"); function withInvoiceSystemLoading(Component) { const WrappedComponent = /* @__PURE__ */ __name((props) => { const { isLoading, error } = useInvoiceSystem(); const enhancedProps = { ...props, isLoading, error }; return /* @__PURE__ */ React.createElement(Component, enhancedProps); }, "WrappedComponent"); WrappedComponent.displayName = `withInvoiceSystemLoading(${Component.displayName || Component.name})`; return WrappedComponent; } __name(withInvoiceSystemLoading, "withInvoiceSystemLoading"); var DEFAULT_BRIDGE_CONFIG = { timeout: 3e4, logErrors: true }; function createEffectBridge(runtime, config = {}) { const finalConfig = { ...DEFAULT_BRIDGE_CONFIG, ...config }; return /* @__PURE__ */ __name(async function bridgeEffect(effect) { try { const timeoutEffect = Effect.timeout(effect, finalConfig.timeout); return await runtime.runPromise(timeoutEffect); } catch (error) { if (finalConfig.logErrors) { console.error("Effect operation failed:", error); } throw error; } }, "bridgeEffect"); } __name(createEffectBridge, "createEffectBridge"); async function runEffectsInParallel(runtime, effects, config = {}) { const bridge = createEffectBridge(runtime, config); return Promise.all(effects.map((effect) => bridge(effect))); } __name(runEffectsInParallel, "runEffectsInParallel"); async function runEffectWithRetry(runtime, effect, options = {}) { const { maxRetries = 3, retryDelay = 1e3, config = {} } = options; const bridge = createEffectBridge(runtime, config); let lastError; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await bridge(effect); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (attempt < maxRetries) { await new Promise((resolve) => setTimeout(resolve, retryDelay)); } } } throw lastError; } __name(runEffectWithRetry, "runEffectWithRetry"); function createDebouncedEffectRunner(runtime, delay, config = {}) { const bridge = createEffectBridge(runtime, config); let timeoutId = null; return /* @__PURE__ */ __name(function debouncedRun(effect) { return new Promise((resolve, reject) => { if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { bridge(effect).then(resolve).catch(reject); }, delay); }); }, "debouncedRun"); } __name(createDebouncedEffectRunner, "createDebouncedEffectRunner"); function effectStreamToAsyncIterator(runtime, streamEffect) { let iterator = null; return { async next() { if (!iterator) { const stream = await runtime.runPromise(streamEffect); iterator = stream[Symbol.asyncIterator](); } return iterator.next(); }, [Symbol.asyncIterator]() { return this; } }; } __name(effectStreamToAsyncIterator, "effectStreamToAsyncIterator"); function isTimeoutError(error) { return error instanceof Error && (error.message.includes("timeout") || error.message.includes("timed out")); } __name(isTimeoutError, "isTimeoutError"); function createReactSafeEffectRunner(runtime, config = {}) { const bridge = createEffectBridge(runtime, config); return /* @__PURE__ */ __name(async function runSafely(effect, onError) { try { return await bridge(effect); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); if (onError) { onError(err); } return null; } }, "runSafely"); } __name(createReactSafeEffectRunner, "createReactSafeEffectRunner"); // src/index.ts var VERSION = "0.1.0"; var PACKAGE_NAME = "@invoiceddd/react"; export { InvoiceSystemProvider, PACKAGE_NAME, VERSION, createDebouncedEffectRunner, createEffectBridge, createReactSafeEffectRunner, effectStreamToAsyncIterator, isTimeoutError, runEffectWithRetry, runEffectsInParallel, useCreateInvoice, useInvoice, useInvoiceSystem, useInvoices, useRequestInvoice, withInvoiceSystem, withInvoiceSystemLoading };