{"_id":"@c-a-f/infrastructure-react","name":"@c-a-f/infrastructure-react","dist-tags":{"latest":"1.0.4"},"versions":{"1.0.4":{"name":"@c-a-f/infrastructure-react","type":"module","version":"1.0.4","description":"Clean Architecture Frontend (CAF) — React adapters: usePloc, useUseCase, CAFErrorBoundary, DevTools, useRouteManager, useRouteRepository.","keywords":["clean-architecture-frontend","clean-architecture","caf","infrastructure","react","routing","hooks","ploc","state-management","devtools","debugging"],"author":{"name":"ali aslani","email":"aliaslani.mm@gmail.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/ialiaslani/caf.git"},"main":"./.build/index.js","module":"./.build/index.js","types":"./.build/index.d.ts","exports":{".":{"types":"./.build/index.d.ts","import":"./.build/index.js","node":"./.build/index.js","default":"./.build/index.js"}},"scripts":{"build":"tsc --build","start":"tsc --watch","prepublishOnly":"npm run build","test":"vitest run","test:watch":"vitest"},"dependencies":{"@c-a-f/core":"^1.0.3","@c-a-f/devtools":"^1.0.2","react-router-dom":"^6.23.1"},"peerDependencies":{"react":">=16.8.0"},"devDependencies":{"@testing-library/dom":"^10.4.0","@testing-library/jest-dom":"^6.1.5","@testing-library/react":"^16.0.0","happy-dom":"^15.11.7","vitest":"^2.1.0"},"_id":"@c-a-f/infrastructure-react@1.0.4","gitHead":"ca2feba4757534a4a130717ea50c4df2e2ff01e6","bugs":{"url":"https://github.com/ialiaslani/caf/issues"},"homepage":"https://github.com/ialiaslani/caf#readme","_nodeVersion":"22.13.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-EpJbaa2wnUSJPiogSjQNWpabJwlr6VtBIfk/sO7/CxwcjknQ6Sahvjc+1YePG4IVC9tItfLaefoP+SqFr0ypFw==","shasum":"3276fef76718ac392c119f0161430b449f43c175","tarball":"https://registry.npmjs.org/@c-a-f/infrastructure-react/-/infrastructure-react-1.0.4.tgz","fileCount":48,"unpackedSize":49502,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDVHAwqBB5wmTjZe82XPT0X/8hHTd3rxznmNh97AQ6OFAIgdpd8uvYAjsjPal2tA+a/zZ34i+Zn/qlTuwfDDJ5dCKA="}]},"_npmUser":{"name":"ialiaslani","email":"aliaslani.mm@gmail.com"},"directories":{},"maintainers":[{"name":"ialiaslani","email":"aliaslani.mm@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/infrastructure-react_1.0.4_1771671140036_0.4560744754105919"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-21T10:52:19.937Z","1.0.4":"2026-02-21T10:52:20.208Z","modified":"2026-02-21T10:52:20.432Z"},"maintainers":[{"name":"ialiaslani","email":"aliaslani.mm@gmail.com"}],"description":"Clean Architecture Frontend (CAF) — React adapters: usePloc, useUseCase, CAFErrorBoundary, DevTools, useRouteManager, useRouteRepository.","homepage":"https://github.com/ialiaslani/caf#readme","keywords":["clean-architecture-frontend","clean-architecture","caf","infrastructure","react","routing","hooks","ploc","state-management","devtools","debugging"],"repository":{"type":"git","url":"git+https://github.com/ialiaslani/caf.git"},"author":{"name":"ali aslani","email":"aliaslani.mm@gmail.com"},"bugs":{"url":"https://github.com/ialiaslani/caf/issues"},"license":"MIT","readme":"# @c-a-f/infrastructure-react\n\nReact-specific infrastructure adapters for CAF.\n\n## Installation\n\n```bash\nnpm install @c-a-f/infrastructure-react react-router-dom\n```\n\n## Usage\n\n### usePloc\n\nHook that subscribes to a Ploc and returns the current state and the Ploc instance. Subscribes on mount, syncs when the ploc reference changes, and unsubscribes on unmount.\n\n```typescript\nimport { usePloc } from '@c-a-f/infrastructure-react';\n\nfunction UserProfile({ userPloc }: { userPloc: UserPloc }) {\n  const [state, ploc] = usePloc(userPloc);\n\n  return (\n    <div>\n      <span>{state.name}</span>\n      <button onClick={() => ploc.loadUser()}>Refresh</button>\n    </div>\n  );\n}\n```\n\nThe hook returns a tuple `[state, ploc]`: the current state (re-renders when the Ploc updates) and the same Ploc instance so you can call methods on it. The Ploc is typically provided via props, context, or created with `useMemo` for the component tree.\n\n### useUseCase\n\nHook that wraps a UseCase execution with loading/error/data state management. Handles `RequestResult` subscriptions automatically and provides a clean API for executing use cases.\n\n```typescript\nimport { useUseCase } from '@c-a-f/infrastructure-react';\nimport { CreateUser } from './application/User/Commands/CreateUser';\n\nfunction CreateUserForm({ createUserUseCase }: { createUserUseCase: CreateUser }) {\n  const { execute, loading, error, data } = useUseCase(createUserUseCase);\n\n  const handleSubmit = async (e: React.FormEvent) => {\n    e.preventDefault();\n    const formData = new FormData(e.target as HTMLFormElement);\n    const result = await execute({\n      name: formData.get('name') as string,\n      email: formData.get('email') as string,\n    });\n    \n    if (result) {\n      console.log('User created:', result);\n    }\n  };\n\n  return (\n    <form onSubmit={handleSubmit}>\n      {loading && <p>Creating user...</p>}\n      {error && <p>Error: {error.message}</p>}\n      {data && <p>User created: {data.name}</p>}\n      {/* form fields */}\n    </form>\n  );\n}\n```\n\nThe hook automatically subscribes to the `RequestResult`'s `loading`, `data`, and `error` pulses, so your component re-renders when these values change. The `execute` function returns the data value directly (or `null` on error), making it easy to handle results.\n\n### CAFErrorBoundary\n\nError Boundary component that catches errors from Ploc/UseCase execution and component rendering. Provides error context via React Context and supports custom error UI and recovery.\n\n```typescript\nimport { CAFErrorBoundary, useCAFError } from '@c-a-f/infrastructure-react';\n\nfunction App() {\n  return (\n    <CAFErrorBoundary\n      fallback={(error, errorInfo, resetError) => (\n        <div>\n          <h2>Oops! Something went wrong</h2>\n          <p>{error.message}</p>\n          <button onClick={resetError}>Try again</button>\n        </div>\n      )}\n      onError={(error, errorInfo) => {\n        // Log to error reporting service\n        console.error('Error caught:', error, errorInfo);\n      }}\n    >\n      <YourApp />\n    </CAFErrorBoundary>\n  );\n}\n```\n\nAccess error context from anywhere within the boundary:\n\n```typescript\nimport { useCAFError } from '@c-a-f/infrastructure-react';\n\nfunction ErrorDisplay() {\n  const errorContext = useCAFError();\n  \n  if (errorContext?.error) {\n    return (\n      <div>\n        <p>Error: {errorContext.error.message}</p>\n        <button onClick={errorContext.resetError}>Reset</button>\n      </div>\n    );\n  }\n  \n  return null;\n}\n```\n\nThe error boundary catches:\n- Errors during component rendering\n- Errors in lifecycle methods\n- Errors in constructors\n- Errors from Ploc/UseCase execution (when not caught locally)\n\n### DevTools Integration\n\nReact hooks for debugging and inspecting CAF applications. Integrates with `@c-a-f/devtools` and exposes data for React DevTools.\n\n#### usePlocDevTools\n\nHook that provides DevTools for a Ploc instance. Enables state tracking, time-travel debugging, state history, and memory leak detection.\n\n```typescript\nimport { usePloc, usePlocDevTools } from '@c-a-f/infrastructure-react';\nimport { createMemoryLeakDetector } from '@c-a-f/devtools';\n\nfunction UserProfile({ userPloc }: { userPloc: UserPloc }) {\n  const [state, ploc] = usePloc(userPloc);\n  \n  // Optional: Create memory leak detector\n  const leakDetector = createMemoryLeakDetector({\n    enabled: process.env.NODE_ENV === 'development',\n    warnThreshold: 10000, // Warn after 10 seconds\n  });\n  \n  const devTools = usePlocDevTools(ploc, { \n    name: 'UserPloc', \n    enabled: process.env.NODE_ENV === 'development',\n    enableLeakDetection: true,\n    leakDetector, // Optional: provide custom detector\n  });\n\n  // Access state history\n  const history = devTools.getStateHistory();\n  \n  // Time-travel debugging\n  const handleUndo = () => {\n    devTools.previousState();\n  };\n\n  return (\n    <div>\n      <span>{state.name}</span>\n      {process.env.NODE_ENV === 'development' && (\n        <button onClick={handleUndo}>Undo</button>\n      )}\n    </div>\n  );\n}\n```\n\n#### useUseCaseDevTools\n\nHook that provides DevTools for UseCase execution tracking. Tracks execution history, timing, errors, and performance profiling.\n\n```typescript\nimport { useUseCaseDevTools } from '@c-a-f/infrastructure-react';\nimport { createPerformanceProfiler } from '@c-a-f/devtools';\nimport { CreateUser } from './application/User/Commands/CreateUser';\n\nfunction CreateUserForm({ createUserUseCase }: { createUserUseCase: CreateUser }) {\n  // Optional: Create performance profiler\n  const profiler = createPerformanceProfiler({\n    enabled: process.env.NODE_ENV === 'development',\n    trackSlowOperations: true,\n    slowThreshold: 100, // ms\n  });\n  \n  const useCaseDevTools = useUseCaseDevTools({ \n    name: 'CreateUser',\n    enabled: true,\n    logExecutionTime: true,\n    profiler, // Optional: provide custom profiler\n  });\n  \n  // Wrap use case with DevTools tracking\n  const trackedUseCase = useCaseDevTools.wrap(createUserUseCase);\n  \n  // Get execution statistics\n  const stats = useCaseDevTools.getStatistics();\n  console.log('Total executions:', stats.totalExecutions);\n  console.log('Average duration:', stats.averageDuration, 'ms');\n  \n  // Use the tracked use case...\n}\n```\n\n#### useCAFDevTools\n\nMain hook that provides centralized DevTools access for your entire application. Tracks all Plocs and UseCases.\n\n```typescript\nimport { useCAFDevTools, useTrackPloc } from '@c-a-f/infrastructure-react';\n\nfunction App() {\n  const devTools = useCAFDevTools({ \n    enabled: process.env.NODE_ENV === 'development' \n  });\n\n  // Enable/disable globally\n  const handleToggleDevTools = () => {\n    if (devTools.enabled) {\n      devTools.disable();\n    } else {\n      devTools.enable();\n    }\n  };\n\n  return (\n    <div>\n      {/* Your app */}\n      {process.env.NODE_ENV === 'development' && (\n        <button onClick={handleToggleDevTools}>\n          {devTools.enabled ? 'Disable' : 'Enable'} DevTools\n        </button>\n      )}\n    </div>\n  );\n}\n\n// In components using Plocs, automatically track them:\nfunction UserComponent({ userPloc }: { userPloc: UserPloc }) {\n  useTrackPloc(userPloc, 'UserPloc'); // Automatically registered with DevTools\n  const [state] = usePloc(userPloc);\n  // ...\n}\n```\n\nThe DevTools data is also exposed to `window.__CAF_DEVTOOLS__` for React DevTools extension integration.\n\n### CAFProvider (Ploc/UseCase provisioning)\n\nRegister Plocs and UseCases at the app root so any descendant can access them without prop drilling. Use a single provider with all keys, or nest providers for feature-specific instances.\n\n**Wiring at app root:** Create your Plocs and UseCases once (e.g. in the root component or a bootstrap module), pass them into `CAFProvider` by key, and wrap your app. Any descendant can then read them via `usePlocFromContext(key)` or `useUseCaseFromContext(key)` without prop drilling.\n\n**Minimal example (wrap app, inject Ploc, consume in child):**\n\n```tsx\nimport { CAFProvider, usePlocFromContext, usePloc } from '@c-a-f/infrastructure-react';\n\n// Root: wrap app and inject Plocs by key\nfunction main() {\n  const counterPloc = new CounterPloc(0);\n  root.render(\n    <CAFProvider plocs={{ counter: counterPloc }}>\n      <App />\n    </CAFProvider>\n  );\n}\n\n// Child: consume from context (no props)\nfunction Counter() {\n  const ploc = usePlocFromContext<CounterPloc>('counter');\n  if (!ploc) return null;\n  const [state, p] = usePloc(ploc);\n  return <button onClick={() => p.increment()}>{state}</button>;\n}\n```\n\n**Recommended: single provider at root**\n\n```typescript\nimport { CAFProvider, usePlocFromContext, useUseCaseFromContext, usePloc, useUseCase } from '@c-a-f/infrastructure-react';\n\n// At app root: create Plocs/UseCases (e.g. with useMemo) and pass by key\nfunction AppRoot() {\n  const userPloc = useMemo(() => new UserPloc(userRepo), [userRepo]);\n  const createUser = useMemo(() => new CreateUser(repo), [repo]);\n\n  return (\n    <CAFProvider plocs={{ user: userPloc }} useCases={{ createUser }}>\n      <App />\n    </CAFProvider>\n  );\n}\n\n// In any descendant: typed hooks (return undefined if key not registered)\nfunction UserProfile() {\n  const userPloc = usePlocFromContext<UserPloc>('user');\n  if (!userPloc) return null;\n  const [state, ploc] = usePloc(userPloc);\n  return <span>{state.name}</span>;\n}\n\nfunction CreateUserForm() {\n  const createUser = useUseCaseFromContext<[CreateUserInput], User>('createUser');\n  if (!createUser) return null;\n  const { execute, loading, error } = useUseCase(createUser);\n  // ...\n}\n```\n\nYou can also use `useCAFContext()` and read `.plocs[key]` / `.useCases[key]` when you need the raw registry. When the key is missing or outside a provider, `usePlocFromContext` and `useUseCaseFromContext` return `undefined` (no throw).\n\n**Nested providers:** Inner provider does not merge with outer; children see only the nearest provider’s `plocs` / `useCases`. Prefer one root provider with all keys.\n\n### useRouteManager\n\nHook that provides a `RouteManager` from `@c-a-f/core`:\n\n```typescript\nimport { useRouteManager } from '@c-a-f/infrastructure-react';\nimport { RouteManagerAuthOptions } from '@c-a-f/core';\n\nfunction MyComponent() {\n  // Optional: provide auth configuration\n  const authOptions: RouteManagerAuthOptions = {\n    loginPath: '/login',\n    isLoggedIn: () => !!localStorage.getItem('token'),\n  };\n  \n  const routeManager = useRouteManager(authOptions);\n  \n  const handleLogin = async () => {\n    // ... login logic\n    routeManager.changeRoute('/dashboard');\n  };\n  \n  return <button onClick={handleLogin}>Login</button>;\n}\n```\n\n### useRouteRepository\n\nHook that provides a `RouteRepository` implementation:\n\n```typescript\nimport { useRouteRepository } from '@c-a-f/infrastructure-react';\nimport { RouteManager } from '@c-a-f/core';\n\nfunction MyComponent() {\n  const routeRepository = useRouteRepository();\n  const routeManager = new RouteManager(routeRepository);\n  \n  // Use routeManager...\n}\n```\n\n## Exports\n\n- `usePloc` — Hook that subscribes to a Ploc and returns `[state, ploc]`; handles subscribe/unsubscribe and cleanup\n- `useUseCase` — Hook that wraps UseCase execution with loading/error/data state management; handles RequestResult subscriptions automatically\n- `CAFErrorBoundary` — Error Boundary component that catches errors from Ploc/UseCase execution; provides error context via React Context\n- `useCAFError` — Hook to access error context from CAFErrorBoundary\n- `CAFProvider` — Root-level provider for Plocs and UseCases (by key); descendants access via `useCAFContext()` or typed hooks\n- `useCAFContext` — Hook to read the CAF context (`plocs` and `useCases` registries from the nearest `CAFProvider`)\n- `usePlocFromContext` — Hook to get a Ploc by key from context; returns `undefined` if key not registered (generic for type safety)\n- `useUseCaseFromContext` — Hook to get a UseCase by key from context; returns `undefined` if key not registered (generics for args/result)\n- `CAFContext` — React context used by `CAFProvider` (for advanced use)\n- `usePlocDevTools` — Hook that provides DevTools for a Ploc instance; enables state tracking and time-travel debugging\n- `useUseCaseDevTools` — Hook that provides DevTools for UseCase execution tracking\n- `useCAFDevTools` — Main hook that provides centralized DevTools access; tracks all Plocs and UseCases\n- `useTrackPloc` — Helper hook to automatically register a Ploc with DevTools\n- `useRouteManager` — Hook returning core `RouteManager` with React Router integration\n- `useRouteRepository` — Hook returning `RouteRepository` implementation\n\n## Dependencies\n\n- `@c-a-f/core` — Core primitives\n- `@c-a-f/devtools` — DevTools utilities (for debugging)\n- `react-router-dom` — React Router\n\n## Peer Dependencies\n\n- `react` >= 16.8.0\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-129d34ba8b5ae64b4ab36b48830af2a3"}