@invoiceddd/react
Version:
React integration utilities for InvoiceDDD system
431 lines (426 loc) • 12.6 kB
JavaScript
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 };