@invoiceddd/react
Version:
React integration utilities for InvoiceDDD system
478 lines (467 loc) • 14.2 kB
TypeScript
import * as react_jsx_runtime from 'react/jsx-runtime';
import { ManagedRuntime, Effect } from 'effect';
import { InvoiceSystemOptions, InvoiceSystemInstance, AppConfig } from 'invoiceddd';
import { ReactNode, ComponentType } from 'react';
import { CreateInvoiceOutput, CreateInvoiceInput, InvoiceDraft, RequestInvoiceInput, GetInvoiceInput, GetInvoiceOutput, ListInvoicesInput, ListInvoicesOutput } from '@invoiceddd/application';
/**
* Context value containing the invoice system instance and utilities
*/
interface InvoiceSystemContextValue {
/** The invoice system instance */
system: InvoiceSystemInstance | null;
/** The current configuration */
config: AppConfig | null;
/** Whether the system is currently loading */
isLoading: boolean;
/** Any error that occurred during initialization */
error: Error | null;
/** The Effect runtime for advanced usage */
runtime: ManagedRuntime.ManagedRuntime<unknown, Error> | null;
}
/**
* Props for the InvoiceSystemProvider component
*/
interface InvoiceSystemProviderProps {
/** Child components */
children: ReactNode;
/** Options for creating the invoice system */
options?: InvoiceSystemOptions;
/** Whether to auto-start the system on mount */
autoStart?: boolean;
}
/**
* Provider component that initializes and provides the InvoiceDDD system to React components
*
* @example
* ```tsx
* function App() {
* return (
* <InvoiceSystemProvider
* options={{
* config: { database: { url: 'file:./invoices.db' } },
* autoStartServer: false
* }}
* autoStart={true}
* >
* <MyInvoiceApp />
* </InvoiceSystemProvider>
* );
* }
* ```
*/
declare function InvoiceSystemProvider({ children, options, autoStart, }: InvoiceSystemProviderProps): react_jsx_runtime.JSX.Element;
/**
* Hook to access the invoice system from React components
*
* @throws {Error} If used outside of InvoiceSystemProvider
*
* @example
* ```tsx
* function InvoiceList() {
* const { system, isLoading, error } = useInvoiceSystem();
*
* if (isLoading) return <div>Loading...</div>;
* if (error) return <div>Error: {error.message}</div>;
* if (!system) return <div>System not available</div>;
*
* // Use system for invoice operations
* return <div>Invoice system ready!</div>;
* }
* ```
*/
declare function useInvoiceSystem(): InvoiceSystemContextValue;
/**
* @fileoverview React Hooks for InvoiceDDD Operations
*
* Provides React hooks for common invoice operations with proper state management
* and Effect integration.
*/
/**
* Hook for creating invoices with state management
*
* @example
* ```tsx
* function CreateInvoiceForm() {
* const { createInvoice, isLoading, error } = useCreateInvoice();
*
* const handleSubmit = async (input: CreateInvoiceInput) => {
* const result = await createInvoice(input);
* if (result) {
* console.log('Invoice created:', result.invoice.invoiceId);
* }
* };
*
* return (
* <form onSubmit={handleSubmit}>
* {isLoading && <div>Creating invoice...</div>}
* {error && <div>Error: {error.message}</div>}
* </form>
* );
* }
* ```
*/
declare function useCreateInvoice(): {
data: CreateInvoiceOutput | null;
isLoading: boolean;
error: Error | null;
createInvoice: (input: CreateInvoiceInput) => Promise<CreateInvoiceOutput | null>;
};
/**
* Hook for requesting invoice drafts
*
* @example
* ```tsx
* function RequestInvoiceButton() {
* const { requestInvoice, isLoading } = useRequestInvoice();
*
* const handleRequest = async () => {
* const input: RequestInvoiceInput = {
* orderId: "order-123",
* requestedBy: "user-456"
* };
* await requestInvoice(input);
* };
*
* return (
* <button onClick={handleRequest} disabled={isLoading}>
* {isLoading ? 'Requesting...' : 'Request Invoice'}
* </button>
* );
* }
* ```
*/
declare function useRequestInvoice(): {
data: InvoiceDraft | null;
isLoading: boolean;
error: Error | null;
requestInvoice: (input: RequestInvoiceInput) => Promise<InvoiceDraft | null>;
};
/**
* Hook for fetching a single invoice by ID or invoice number
*
* @example
* ```tsx
* function InvoiceDetail({ orderId }: { orderId: string }) {
* const { invoice, isLoading, error, refetch } = useInvoice({ orderId });
*
* if (isLoading) return <div>Loading invoice...</div>;
* if (error) return <div>Error: {error.message}</div>;
* if (!invoice) return <div>Invoice not found</div>;
*
* return (
* <div>
* <h1>Invoice {invoice.invoice.invoiceNumber}</h1>
* <button onClick={refetch}>Refresh</button>
* </div>
* );
* }
* ```
*/
declare function useInvoice(input: GetInvoiceInput): {
invoice: GetInvoiceOutput | null;
isLoading: boolean;
error: Error | null;
refetch: () => Promise<void>;
};
/**
* Hook for listing invoices with pagination and filtering
*
* @example
* ```tsx
* function InvoiceList() {
* const {
* invoices,
* isLoading,
* error,
* loadMore,
* hasMore,
* refetch
* } = useInvoices({
* pagination: { limit: 10 },
* filters: { createdBy: "user-123" }
* });
*
* if (isLoading && !invoices) return <div>Loading...</div>;
* if (error) return <div>Error: {error.message}</div>;
*
* return (
* <div>
* {invoices?.invoices.map(invoice => (
* <div key={invoice.invoiceId}>{invoice.invoiceNumber}</div>
* ))}
* {hasMore && (
* <button onClick={loadMore} disabled={isLoading}>
* {isLoading ? 'Loading...' : 'Load More'}
* </button>
* )}
* <button onClick={refetch}>Refresh</button>
* </div>
* );
* }
* ```
*/
declare function useInvoices(input?: ListInvoicesInput): {
invoices: ListInvoicesOutput | null;
isLoading: boolean;
error: Error | null;
loadMore: () => void;
hasMore: boolean;
refetch: () => void;
};
/**
* @fileoverview Higher-Order Components for InvoiceDDD Integration
*
* Provides HOCs for integrating Effect-based invoice operations with React components.
*/
/**
* Props injected by withInvoiceSystem HOC
*/
interface WithInvoiceSystemProps {
/** The Effect runtime for running invoice operations */
invoiceRuntime: ManagedRuntime.ManagedRuntime<unknown, Error>;
/** Helper function to run Effect operations and return promises */
runInvoiceEffect: <A>(effect: Effect.Effect<A, Error, any>) => Promise<A>;
}
/**
* Higher-order component that injects invoice system runtime and utilities
*
* @example
* ```tsx
* interface MyComponentProps {
* title: string;
* }
*
* function MyComponent({
* title,
* invoiceRuntime,
* runInvoiceEffect
* }: MyComponentProps & WithInvoiceSystemProps) {
* const handleCreateInvoice = async () => {
* const effect = Effect.gen(function* () {
* const createUseCase = yield* CreateInvoiceUseCase;
* return yield* createUseCase.execute(orderData);
* });
*
* const invoice = await runInvoiceEffect(effect);
* console.log('Created invoice:', invoice);
* };
*
* return (
* <div>
* <h1>{title}</h1>
* <button onClick={handleCreateInvoice}>Create Invoice</button>
* </div>
* );
* }
*
* export default withInvoiceSystem(MyComponent);
* ```
*/
declare function withInvoiceSystem<P extends object>(Component: ComponentType<P & WithInvoiceSystemProps>): ComponentType<P>;
/**
* Props for components that need loading and error states
*/
interface WithLoadingProps {
isLoading: boolean;
error: Error | null;
}
/**
* Higher-order component that provides loading and error state management
*
* @example
* ```tsx
* function MyComponent({
* data,
* isLoading,
* error
* }: { data: string } & WithLoadingProps) {
* if (isLoading) return <div>Loading...</div>;
* if (error) return <div>Error: {error.message}</div>;
*
* return <div>Data: {data}</div>;
* }
*
* export default withInvoiceSystemLoading(MyComponent);
* ```
*/
declare function withInvoiceSystemLoading<P extends object>(Component: ComponentType<P & WithLoadingProps>): ComponentType<P>;
/**
* @fileoverview Utility functions for Effect-to-Promise bridge
*
* Provides utilities for converting Effect operations to Promises for easier
* integration with React components and standard JavaScript async patterns.
*/
/**
* Configuration for Effect-to-Promise conversion
*/
interface EffectBridgeConfig {
/** Timeout in milliseconds for Effect operations */
timeout?: number;
/** Whether to log errors to console */
logErrors?: boolean;
}
/**
* Creates a bridge function that converts Effect operations to Promises
*
* @example
* ```tsx
* function MyComponent() {
* const { runtime } = useInvoiceSystem();
* const bridge = createEffectBridge(runtime);
*
* const handleOperation = async () => {
* try {
* const result = await bridge(myEffect);
* console.log('Success:', result);
* } catch (error) {
* console.error('Failed:', error);
* }
* };
*
* return <button onClick={handleOperation}>Run Operation</button>;
* }
* ```
*/
declare function createEffectBridge(runtime: ManagedRuntime.ManagedRuntime<any, Error>, config?: EffectBridgeConfig): <A, E, R>(effect: Effect.Effect<A, E, R>) => Promise<A>;
/**
* Utility for running multiple Effect operations in parallel and converting to Promise
*
* @example
* ```tsx
* const results = await runEffectsInParallel(runtime, [
* getInvoiceEffect,
* getOrderEffect,
* getCustomerEffect
* ]);
* ```
*/
declare function runEffectsInParallel<A>(runtime: ManagedRuntime.ManagedRuntime<any, Error>, effects: Effect.Effect<A, Error, any>[], config?: EffectBridgeConfig): Promise<A[]>;
/**
* Utility for running Effect operations with retry logic
*
* @example
* ```tsx
* const result = await runEffectWithRetry(
* runtime,
* unstableEffect,
* { maxRetries: 3, retryDelay: 1000 }
* );
* ```
*/
declare function runEffectWithRetry<A, E, R>(runtime: ManagedRuntime.ManagedRuntime<any, Error>, effect: Effect.Effect<A, E, R>, options?: {
maxRetries?: number;
retryDelay?: number;
config?: EffectBridgeConfig;
}): Promise<A>;
/**
* Creates a debounced Effect runner for UI operations
*
* @example
* ```tsx
* function SearchComponent() {
* const { runtime } = useInvoiceSystem();
* const debouncedSearch = createDebouncedEffectRunner(runtime, 300);
*
* const handleSearch = (query: string) => {
* debouncedSearch(searchInvoicesEffect(query))
* .then(results => setSearchResults(results))
* .catch(error => setError(error));
* };
*
* return <input onChange={e => handleSearch(e.target.value)} />;
* }
* ```
*/
declare function createDebouncedEffectRunner(runtime: ManagedRuntime.ManagedRuntime<any, Error>, delay: number, config?: EffectBridgeConfig): <A, E, R>(effect: Effect.Effect<A, E, R>) => Promise<A>;
/**
* Utility for converting Effect streams to async iterators for React
*
* @example
* ```tsx
* function StreamingComponent() {
* const { runtime } = useInvoiceSystem();
* const [items, setItems] = useState([]);
*
* useEffect(() => {
* const processStream = async () => {
* const iterator = effectStreamToAsyncIterator(runtime, invoiceStream);
*
* for await (const invoice of iterator) {
* setItems(prev => [...prev, invoice]);
* }
* };
*
* processStream();
* }, [runtime]);
*
* return <div>{items.map(item => <div key={item.id}>{item.name}</div>)}</div>;
* }
* ```
*/
declare function effectStreamToAsyncIterator<A, E, R>(runtime: ManagedRuntime.ManagedRuntime<any, Error>, streamEffect: Effect.Effect<AsyncIterable<A>, E, R>): AsyncIterableIterator<A>;
/**
* Type guard to check if an error is an Effect timeout error
*/
declare function isTimeoutError(error: unknown): boolean;
/**
* Utility to wrap Effect operations with React-friendly error handling
*/
declare function createReactSafeEffectRunner(runtime: ManagedRuntime.ManagedRuntime<any, Error>, config?: EffectBridgeConfig): <A, E, R>(effect: Effect.Effect<A, E, R>, onError?: (error: Error) => void) => Promise<A | null>;
/**
* @fileoverview React Integration for InvoiceDDD
*
* Provides React hooks, components, and utilities for integrating the InvoiceDDD
* system with React applications.
*
* @example
* ```tsx
* import { InvoiceSystemProvider, useInvoiceSystem, useCreateInvoice } from '@invoiceddd/react';
*
* function App() {
* return (
* <InvoiceSystemProvider options={{ config: { database: { url: 'file:./invoices.db' } } }}>
* <InvoiceApp />
* </InvoiceSystemProvider>
* );
* }
*
* function InvoiceApp() {
* const { system, isLoading } = useInvoiceSystem();
* const { createInvoice } = useCreateInvoice();
*
* if (isLoading) return <div>Loading...</div>;
*
* return <div>Invoice system ready!</div>;
* }
* ```
*
* @packageDocumentation
* @since 0.1.0
*/
/**
* React context and provider for invoice system integration
*
* @example
* ```tsx
* import { InvoiceSystemProvider, useInvoiceSystem } from '@invoiceddd/react';
*
* function App() {
* return (
* <InvoiceSystemProvider
* options={{
* config: { database: { url: 'file:./invoices.db' } },
* autoStartServer: false
* }}
* >
* <MyApp />
* </InvoiceSystemProvider>
* );
* }
* ```
*/
/**
* Package version and metadata
*/
declare const VERSION = "0.1.0";
declare const PACKAGE_NAME = "@invoiceddd/react";
export { type EffectBridgeConfig, type InvoiceSystemContextValue, InvoiceSystemProvider, type InvoiceSystemProviderProps, PACKAGE_NAME, VERSION, type WithInvoiceSystemProps, type WithLoadingProps, createDebouncedEffectRunner, createEffectBridge, createReactSafeEffectRunner, effectStreamToAsyncIterator, isTimeoutError, runEffectWithRetry, runEffectsInParallel, useCreateInvoice, useInvoice, useInvoiceSystem, useInvoices, useRequestInvoice, withInvoiceSystem, withInvoiceSystemLoading };