{"_id":"@databite/flow","_rev":"4-d8fbd18093d66acb869a8cd17181d218","name":"@databite/flow","dist-tags":{"latest":"2.1.0"},"versions":{"1.0.0":{"name":"@databite/flow","version":"1.0.0","keywords":["flow","sdk","integration","api","scheduler","flow","typescript"],"author":{"name":"Databite"},"license":"MIT","_id":"@databite/flow@1.0.0","maintainers":[{"name":"david-mungai","email":"david.kinuthia.mungai@gmail.com"}],"homepage":"https://github.com/databite/builder#readme","bugs":{"url":"https://github.com/databite/builder/issues"},"dist":{"shasum":"d1dc9bc1b859446c26fa92d3714bd98bf5743264","tarball":"https://registry.npmjs.org/@databite/flow/-/flow-1.0.0.tgz","fileCount":43,"integrity":"sha512-KeK11SetqiO21H75dEP5d70cbEmJUvBxGAtC5fN3SL4sEUYS3il4LM6okImV5CjeNWMnCqdEGUAkCTD8dUihCg==","signatures":[{"sig":"MEQCIAr+2ekuGQyPf+9xTaTZeASopOpdugGtzVfpQtsICBAgAiAbyyPwT9XjkfhlR27se9J894d15exYY+BrBcQuHfd6rw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1327506},"main":"dist/index.js","type":"module","_from":"file:databite-flow-1.0.0.tgz","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"rollup -c -w","build":"rollup -c","clean":"rimraf dist","build:watch":"rollup -c -w"},"_npmUser":{"name":"david-mungai","email":"david.kinuthia.mungai@gmail.com"},"_resolved":"C:\\Users\\david\\AppData\\Local\\Temp\\5766c2e3c427f4e2161d311bfa12972f\\databite-flow-1.0.0.tgz","_integrity":"sha512-KeK11SetqiO21H75dEP5d70cbEmJUvBxGAtC5fN3SL4sEUYS3il4LM6okImV5CjeNWMnCqdEGUAkCTD8dUihCg==","repository":{"url":"git+https://github.com/databite/builder.git","type":"git"},"_npmVersion":"11.5.2","description":"Complex workflow builder for Databite.","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^4.1.11","clsx":"^2.1.1","react":"^19.1.1","react-dom":"^19.1.1","tailwindcss":"^4.1.13","lucide-react":"^0.544.0","tailwind-merge":"^3.3.1","@databite/types":"1.0.0","react-hook-form":"^7.63.0","@tailwindcss/vite":"^4.1.13","@hookform/resolvers":"^5.2.2","@radix-ui/react-slot":"^1.2.3","@radix-ui/react-label":"^2.1.7","@radix-ui/react-dialog":"^1.1.15","@radix-ui/react-select":"^2.2.6","@radix-ui/react-switch":"^1.2.6","class-variance-authority":"^0.7.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.2","eslint":"^9.36.0","rimraf":"^6.0.0","rollup":"^4.52.4","globals":"^16.4.0","postcss":"^8.5.6","copyfiles":"^2.4.1","@eslint/js":"^9.36.0","typescript":"~5.8.3","@types/node":"^24.5.2","@types/react":"^19.1.13","autoprefixer":"^10.4.21","tw-animate-css":"^1.4.0","@types/react-dom":"^19.1.9","typescript-eslint":"^8.44.0","@tailwindcss/postcss":"^4.1.14","@vitejs/plugin-react":"^5.0.3","rollup-plugin-postcss":"^4.0.2","@rollup/plugin-commonjs":"^28.0.6","@rollup/plugin-typescript":"^12.1.4","eslint-plugin-react-hooks":"^5.2.0","@rollup/plugin-node-resolve":"^16.0.2","eslint-plugin-react-refresh":"^0.4.20","rollup-plugin-peer-deps-external":"^2.2.4"},"peerDependencies":{"typescript":">=4.5.0"},"_npmOperationalInternal":{"tmp":"tmp/flow_1.0.0_1759840742032_0.5705058751951595","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"2.0.0":{"name":"@databite/flow","version":"2.0.0","keywords":["flow","sdk","integration","api","scheduler","flow","typescript"],"author":{"name":"Databite"},"license":"MIT","_id":"@databite/flow@2.0.0","maintainers":[{"name":"david-mungai","email":"david.kinuthia.mungai@gmail.com"}],"homepage":"https://github.com/databite/builder#readme","bugs":{"url":"https://github.com/databite/builder/issues"},"dist":{"shasum":"322d9a2f58b11744f1e82d8c8a0bb426a754b02c","tarball":"https://registry.npmjs.org/@databite/flow/-/flow-2.0.0.tgz","fileCount":51,"integrity":"sha512-knfFNVx8kP60o3AzYfp9Gc3Y0wQhIMVso1lNRfru/Umilq4PgI/g6NVtxxmP4rkzdO4SyB2u3upvyoS9/iM0zw==","signatures":[{"sig":"MEQCIHrgL2v5HC2NNa4U0UYhysvDfC/5b2h8lxQwAiUlcWXxAiBNEA9a/tycV0D/yPsZelYX2SS/2zU8DGaVzxGNM/O5BQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1386614},"main":"dist/index.js","type":"module","_from":"file:databite-flow-2.0.0.tgz","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"default":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"react-server":{"types":"./dist/server.d.ts","import":"./dist/server.js","require":"./dist/server.cjs"}}},"scripts":{"dev":"rollup -c -w","build":"rollup -c","clean":"rimraf dist","build:watch":"rollup -c -w"},"_npmUser":{"name":"david-mungai","email":"david.kinuthia.mungai@gmail.com"},"_resolved":"C:\\Users\\david\\AppData\\Local\\Temp\\b760d8e65761479cbaff75a6eaad227e\\databite-flow-2.0.0.tgz","_integrity":"sha512-knfFNVx8kP60o3AzYfp9Gc3Y0wQhIMVso1lNRfru/Umilq4PgI/g6NVtxxmP4rkzdO4SyB2u3upvyoS9/iM0zw==","repository":{"url":"git+https://github.com/databite/builder.git","type":"git"},"_npmVersion":"11.5.2","description":"Complex workflow builder for Databite.","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^4.1.11","clsx":"^2.1.1","react":"^19.1.1","react-dom":"^19.1.1","tailwindcss":"^4.1.13","lucide-react":"^0.544.0","tailwind-merge":"^3.3.1","@databite/types":"2.0.0","react-hook-form":"^7.63.0","@tailwindcss/vite":"^4.1.13","@hookform/resolvers":"^5.2.2","@radix-ui/react-slot":"^1.2.3","@radix-ui/react-label":"^2.1.7","@radix-ui/react-dialog":"^1.1.15","@radix-ui/react-select":"^2.2.6","@radix-ui/react-switch":"^1.2.6","class-variance-authority":"^0.7.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.2","eslint":"^9.36.0","rimraf":"^6.0.0","rollup":"^4.52.4","globals":"^16.4.0","postcss":"^8.5.6","copyfiles":"^2.4.1","@eslint/js":"^9.36.0","typescript":"~5.8.3","@types/node":"^24.5.2","@types/react":"^19.1.13","autoprefixer":"^10.4.21","tw-animate-css":"^1.4.0","@types/react-dom":"^19.1.9","typescript-eslint":"^8.44.0","@tailwindcss/postcss":"^4.1.14","@vitejs/plugin-react":"^5.0.3","rollup-plugin-postcss":"^4.0.2","@rollup/plugin-commonjs":"^28.0.6","@rollup/plugin-typescript":"^12.1.4","eslint-plugin-react-hooks":"^5.2.0","@rollup/plugin-node-resolve":"^16.0.2","eslint-plugin-react-refresh":"^0.4.20","rollup-plugin-peer-deps-external":"^2.2.4"},"peerDependencies":{"typescript":">=4.5.0"},"_npmOperationalInternal":{"tmp":"tmp/flow_2.0.0_1759890847275_0.517942794674527","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"2.1.0":{"name":"@databite/flow","version":"2.1.0","keywords":["flow","sdk","integration","api","scheduler","flow","typescript"],"author":{"name":"Databite"},"license":"MIT","_id":"@databite/flow@2.1.0","maintainers":[{"name":"david-mungai","email":"david.kinuthia.mungai@gmail.com"}],"homepage":"https://github.com/DatabiteDev/databite#readme","bugs":{"url":"https://github.com/DatabiteDev/databite/issues"},"dist":{"shasum":"78d3ee40a6af7e014fcb69e5b8a31d9bba66f84b","tarball":"https://registry.npmjs.org/@databite/flow/-/flow-2.1.0.tgz","fileCount":51,"integrity":"sha512-EFH0816QwXFgd7RDuzVQ2QOWkRn2erEDtBNyKN5+19P0pNzTrYbCPOEk5IWo2ASmkE1uSnoQtgRLrcwh/Nf/Dg==","signatures":[{"sig":"MEYCIQD5FyDa5sSaTrqMhMypIXZLkwxJ2/xF+kwXzPjsm+7KSwIhAK21CwaCWghTGxhVJi5hvmnCHW+rTYIJnjowLR+ITp+y","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1431622},"main":"dist/index.js","type":"module","_from":"file:databite-flow-2.1.0.tgz","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"default":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"react-server":{"types":"./dist/server.d.ts","import":"./dist/server.js","require":"./dist/server.cjs"}}},"scripts":{"dev":"rollup -c -w","build":"rollup -c","clean":"rimraf dist","build:watch":"rollup -c -w"},"_npmUser":{"name":"david-mungai","email":"david.kinuthia.mungai@gmail.com"},"_resolved":"C:\\Users\\david\\AppData\\Local\\Temp\\1f67df752b426298471ca1441f019a7e\\databite-flow-2.1.0.tgz","_integrity":"sha512-EFH0816QwXFgd7RDuzVQ2QOWkRn2erEDtBNyKN5+19P0pNzTrYbCPOEk5IWo2ASmkE1uSnoQtgRLrcwh/Nf/Dg==","repository":{"url":"git+https://github.com/DatabiteDev/databite.git","type":"git"},"_npmVersion":"11.5.2","description":"Complex workflow builder for Databite.","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^4.1.11","clsx":"^2.1.1","react":"^19.1.1","react-dom":"^19.1.1","tailwindcss":"^4.1.13","lucide-react":"^0.544.0","tailwind-merge":"^3.3.1","@databite/types":"2.1.0","react-hook-form":"^7.63.0","@tailwindcss/vite":"^4.1.13","@hookform/resolvers":"^5.2.2","@radix-ui/react-slot":"^1.2.3","@radix-ui/react-label":"^2.1.7","@radix-ui/react-dialog":"^1.1.15","@radix-ui/react-select":"^2.2.6","@radix-ui/react-switch":"^1.2.6","class-variance-authority":"^0.7.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.2","eslint":"^9.36.0","rimraf":"^6.0.0","rollup":"^4.52.4","globals":"^16.4.0","postcss":"^8.5.6","copyfiles":"^2.4.1","@eslint/js":"^9.36.0","typescript":"~5.8.3","@types/node":"^24.5.2","@types/react":"^19.1.13","autoprefixer":"^10.4.21","tw-animate-css":"^1.4.0","@types/react-dom":"^19.1.9","typescript-eslint":"^8.44.0","@tailwindcss/postcss":"^4.1.14","@vitejs/plugin-react":"^5.0.3","rollup-plugin-postcss":"^4.0.2","@rollup/plugin-commonjs":"^28.0.6","@rollup/plugin-typescript":"^12.1.4","eslint-plugin-react-hooks":"^5.2.0","@rollup/plugin-node-resolve":"^16.0.2","eslint-plugin-react-refresh":"^0.4.20","rollup-plugin-peer-deps-external":"^2.2.4"},"peerDependencies":{"typescript":">=4.5.0"},"_npmOperationalInternal":{"tmp":"tmp/flow_2.1.0_1760665692790_0.40762209683655604","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."}},"time":{"created":"2025-10-07T12:39:01.957Z","modified":"2025-10-27T13:09:16.660Z","1.0.0":"2025-10-07T12:39:02.288Z","2.0.0":"2025-10-08T02:34:07.529Z","2.1.0":"2025-10-17T01:48:12.986Z"},"bugs":{"url":"https://github.com/DatabiteDev/databite/issues"},"author":{"name":"Databite"},"license":"MIT","homepage":"https://github.com/DatabiteDev/databite#readme","keywords":["flow","sdk","integration","api","scheduler","flow","typescript"],"repository":{"url":"git+https://github.com/DatabiteDev/databite.git","type":"git"},"description":"Complex workflow builder for Databite.","maintainers":[{"name":"david-mungai","email":"david.kinuthia.mungai@gmail.com"}],"readme":"# @databite/flow\r\n\r\nA powerful React-based flow engine for building interactive workflows with automatic type inference, IDE autocomplete, and built-in UI components.\r\n\r\n## 📦 Package Structure\r\n\r\n```\r\nflow/\r\n├── src/\r\n│   ├── flow-builder/\r\n│   │   ├── flow-builder.tsx    # Main FlowBuilder class\r\n│   │   └── index.ts            # Flow builder exports\r\n│   ├── flow-execution/\r\n│   │   ├── flow-execution.tsx  # React hooks and components\r\n│   │   ├── flow-hydration.tsx  # Flow hydration utilities\r\n│   │   └── index.ts            # Execution exports\r\n│   ├── components/ui/          # Reusable UI components\r\n│   │   ├── badge.tsx           # Badge component\r\n│   │   ├── button.tsx          # Button component\r\n│   │   ├── card.tsx           # Card component\r\n│   │   ├── dialog.tsx         # Dialog component\r\n│   │   ├── empty.tsx          # Empty state component\r\n│   │   ├── form.tsx           # Form component\r\n│   │   ├── input.tsx          # Input component\r\n│   │   ├── label.tsx          # Label component\r\n│   │   ├── select.tsx         # Select component\r\n│   │   ├── skeleton.tsx       # Skeleton loader\r\n│   │   └── switch.tsx         # Switch component\r\n│   ├── __tests__/             # Test files\r\n│   ├── lib/\r\n│   │   └── utils.ts            # Utility functions\r\n│   ├── index.css              # Global styles\r\n│   ├── index.ts               # Main exports\r\n│   └── server.ts              # Server-side exports\r\n├── dist/                       # Compiled JavaScript output\r\n├── package.json\r\n└── README.md\r\n```\r\n\r\n## 🚀 Installation\r\n\r\n```bash\r\nnpm install @databite/flow @databite/types\r\n```\r\n\r\n**Peer Dependencies:**\r\n\r\n```bash\r\nnpm install react react-dom zod typescript\r\n```\r\n\r\n**Additional Dependencies (included):**\r\n\r\nThe package includes the following UI component dependencies:\r\n\r\n- `@radix-ui/react-dialog` - Dialog components\r\n- `@radix-ui/react-label` - Label components\r\n- `@radix-ui/react-select` - Select components\r\n- `@radix-ui/react-slot` - Slot components\r\n- `@radix-ui/react-switch` - Switch components\r\n- `@hookform/resolvers` - Form validation\r\n- `react-hook-form` - Form handling\r\n- `class-variance-authority` - CSS class utilities\r\n- `clsx` - Conditional class names\r\n- `lucide-react` - Icons\r\n- `tailwind-merge` - Tailwind CSS utilities\r\n- `tailwindcss` - CSS framework\r\n\r\n**Server-Side Support:**\r\n\r\nThe package supports both client and server-side usage with automatic exports:\r\n\r\n- **Client**: Full React components and hooks\r\n- **Server**: Server-safe FlowBuilder (no React dependencies)\r\n- **Automatic**: Package.json exports handle the correct imports\r\n\r\n## 🎯 Overview\r\n\r\nThe `@databite/flow` package provides a React-based fluent API for building interactive workflows with:\r\n\r\n- **Automatic Type Inference**: Each block knows about all previous block outputs\r\n- **IDE Autocomplete**: Full IntelliSense support for flow context\r\n- **Interactive UI Blocks**: Forms, confirmations, and display components\r\n- **Built-in Blocks**: HTTP requests, transformations, delays, and logging\r\n- **React Integration**: Hooks and components for seamless UI integration\r\n- **Custom Blocks**: Extend with your own flow blocks and UI components\r\n- **Error Handling**: Built-in error handling and user feedback\r\n- **Form Validation**: Built-in form validation using react-hook-form\r\n- **UI Components**: Pre-built UI components with Tailwind CSS\r\n\r\n## 📚 API Reference\r\n\r\n### Core Classes\r\n\r\n#### FlowBuilder\r\n\r\nThe main class for building flows with automatic type inference and React integration. This is a server-safe implementation with no React dependencies.\r\n\r\n```typescript\r\nimport { createFlow } from \"@databite/flow\";\r\n\r\nconst flow = createFlow(\"myFlow\")\r\n  .form(\"getUserInput\", {\r\n    fields: [{ name: \"email\", label: \"Email\", type: \"email\" }],\r\n  })\r\n  .http(\"fetchData\", {\r\n    url: (input) => `/api/users/${input.getUserInput.email}`,\r\n    returnType: { id: \"\", name: \"\" },\r\n  })\r\n  .transform(\"processData\", (input) => ({\r\n    processed: input.fetchData.name.toUpperCase(),\r\n  }))\r\n  .build();\r\n```\r\n\r\n#### createFlow Function\r\n\r\nCreates a new flow builder with automatic IDE autocomplete:\r\n\r\n```typescript\r\nexport function createFlow<FlowReturnType extends z.ZodType = z.ZodAny>(\r\n  name: string\r\n): FlowBuilder<FlowReturnType, {}>;\r\n```\r\n\r\n### Builder Methods\r\n\r\n#### Generic Block\r\n\r\nAdd any custom block with automatic type inference:\r\n\r\n```typescript\r\n.block<TName extends string, TOutput>(\r\n  name: TName,\r\n  run: (input: TContext) => Promise<TOutput>,\r\n  options?: {\r\n    requiresInteraction?: boolean;\r\n    label?: string;\r\n    description?: string;\r\n    renderConfig?: any;\r\n  }\r\n): FlowBuilder<FlowReturnType, TContext & { [K in TName]: TOutput }>\r\n```\r\n\r\n#### Form Block\r\n\r\nCreate interactive forms for user input:\r\n\r\n```typescript\r\n.form<TName extends string, TOutput extends Record<string, any>>(\r\n  name: TName,\r\n  config: {\r\n    fields: Array<{\r\n      name: keyof TOutput;\r\n      label: string;\r\n      type?: \"text\" | \"email\" | \"number\" | \"password\" | \"tel\" | \"url\";\r\n      placeholder?: string;\r\n      required?: boolean;\r\n      defaultValue?: any;\r\n    }>;\r\n    title?: string;\r\n    description?: string;\r\n    submitLabel?: string;\r\n  }\r\n): FlowBuilder<FlowReturnType, TContext & { [K in TName]: TOutput }>\r\n```\r\n\r\n#### Confirm Block\r\n\r\nAdd confirmation dialogs:\r\n\r\n```typescript\r\n.confirm<TName extends string>(\r\n  name: TName,\r\n  config: {\r\n    title: string;\r\n    message: string | ((context: TContext) => string);\r\n    confirmLabel?: string;\r\n    cancelLabel?: string;\r\n  }\r\n): FlowBuilder<FlowReturnType, TContext & { [K in TName]: boolean }>\r\n```\r\n\r\n#### Display Block\r\n\r\nShow information to users:\r\n\r\n```typescript\r\n.display<TName extends string>(\r\n  name: TName,\r\n  config: {\r\n    title?: string;\r\n    content: string | ((context: TContext) => string);\r\n    continueLabel?: string;\r\n  }\r\n): FlowBuilder<FlowReturnType, TContext & { [K in TName]: void }>\r\n```\r\n\r\n#### HTTP Block\r\n\r\nMake HTTP requests with automatic error handling:\r\n\r\n```typescript\r\n.http<TName extends string, TOutput extends Record<string, any>>(\r\n  name: TName,\r\n  config: {\r\n    url: string | ((input: TContext) => string);\r\n    returnType: TOutput;\r\n    method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\r\n    headers?: Record<string, string> | ((input: TContext) => Record<string, string>);\r\n    body?: Record<string, any> | ((input: TContext) => Record<string, any>);\r\n    timeout?: number;\r\n  }\r\n): FlowBuilder<FlowReturnType, TContext & { [K in TName]: TOutput }>\r\n```\r\n\r\n#### Transform Block\r\n\r\nTransform data with full type safety:\r\n\r\n```typescript\r\n.transform<TName extends string, TOutput extends Record<string, any>>(\r\n  name: TName,\r\n  transform: (input: TContext) => TOutput | Promise<TOutput>\r\n): FlowBuilder<FlowReturnType, TContext & Record<typeof name, TOutput>>\r\n```\r\n\r\n#### Delay Block\r\n\r\nAdd delays between operations:\r\n\r\n```typescript\r\n.delay<TName extends string>(\r\n  name: TName,\r\n  milliseconds: number\r\n): FlowBuilder<FlowReturnType, TContext & { [K in TName]: TContext }>\r\n```\r\n\r\n#### Log Block\r\n\r\nAdd logging for debugging:\r\n\r\n```typescript\r\n.log<TName extends string>(\r\n  name: TName,\r\n  message?: string | ((input: TContext) => string)\r\n): FlowBuilder<FlowReturnType, TContext & { [K in TName]: TContext }>\r\n```\r\n\r\n#### Returns Method\r\n\r\nSpecify the final return value transformation:\r\n\r\n```typescript\r\n.returns(\r\n  transform: (context: TContext) => z.infer<FlowReturnType>\r\n): FlowBuilder<FlowReturnType, TContext>\r\n```\r\n\r\n### React Integration\r\n\r\n#### useFlowExecution Hook\r\n\r\nManage flow execution state and control with automatic flow hydration:\r\n\r\n```typescript\r\nimport { useFlowExecution } from \"@databite/flow\";\r\n\r\nfunction MyFlowComponent({ flow }) {\r\n  const {\r\n    state, // Current flow state (FlowState)\r\n    currentBlock, // Current block being executed [blockName, block]\r\n    proceed, // Start/continue flow execution\r\n    reset, // Reset flow to beginning\r\n    getResult, // Get final execution result with proper return type transformation\r\n    handleBlockComplete, // Handle interactive block completion\r\n    handleBlockError, // Handle interactive block errors\r\n  } = useFlowExecution(flow);\r\n\r\n  // Auto-proceed for non-interactive blocks\r\n  useEffect(() => {\r\n    if (!state.isExecuting && !state.isComplete) {\r\n      proceed();\r\n    }\r\n  }, [state.isExecuting, state.isComplete, proceed]);\r\n\r\n  return (\r\n    <div>\r\n      <div>\r\n        Step {state.currentStepIndex + 1} of {state.totalSteps}\r\n      </div>\r\n      <div>Current: {state.currentBlockName}</div>\r\n      {state.isComplete && (\r\n        <div>Result: {JSON.stringify(getResult().data)}</div>\r\n      )}\r\n    </div>\r\n  );\r\n}\r\n```\r\n\r\n**FlowState Properties:**\r\n\r\n- `currentStepIndex`: Current step number (0-based)\r\n- `totalSteps`: Total number of steps in the flow\r\n- `currentBlockName`: Name of the current block\r\n- `isExecuting`: Whether the flow is currently executing\r\n- `isComplete`: Whether the flow has completed\r\n- `context`: Current execution context with all block outputs\r\n- `steps`: Array of completed step results\r\n- `startTime`: Timestamp when flow execution started\r\n- `error`: Error message if execution failed\r\n\r\n#### FlowRenderer Component\r\n\r\nAutomatically render flow UI with built-in handling and automatic flow hydration:\r\n\r\n```typescript\r\nimport { FlowRenderer } from \"@databite/flow\";\r\n\r\nfunction App() {\r\n  const flow = createFlow(\"myFlow\")\r\n    .form(\"getInput\", { fields: [{ name: \"name\", label: \"Name\" }] })\r\n    .display(\"showResult\", {\r\n      content: (input) => `Hello ${input.getInput.name}!`,\r\n    })\r\n    .build();\r\n\r\n  return (\r\n    <FlowRenderer\r\n      flow={flow}\r\n      onComplete={(result) => {\r\n        console.log(\"Flow completed:\", result.data);\r\n      }}\r\n    />\r\n  );\r\n}\r\n```\r\n\r\n**FlowRenderer Features:**\r\n\r\n- Automatic flow hydration with render functions\r\n- Auto-proceeds for non-interactive blocks\r\n- Handles block rendering with proper hook isolation\r\n- Calls `onComplete` callback when flow finishes\r\n- Shows skeleton loader during transitions\r\n\r\n## 🎨 UI Components\r\n\r\nThe package includes a comprehensive set of UI components built with Radix UI and Tailwind CSS:\r\n\r\n### Form Components\r\n\r\n- **Form** - Form wrapper with react-hook-form integration\r\n- **Input** - Text input with various types (text, email, number, password, tel, url)\r\n- **Label** - Accessible form labels\r\n- **Select** - Dropdown selection component\r\n\r\n### Display Components\r\n\r\n- **Card** - Container component for content\r\n- **Badge** - Status and label indicators\r\n- **Dialog** - Modal dialogs for confirmations\r\n- **Empty** - Empty state component\r\n- **Skeleton** - Loading state component\r\n\r\n### Interactive Components\r\n\r\n- **Button** - Various button styles and states\r\n- **Switch** - Toggle switch component\r\n\r\n### Styling\r\n\r\nAll components are built with:\r\n\r\n- **Tailwind CSS** - Utility-first CSS framework\r\n- **Radix UI** - Accessible, unstyled UI primitives\r\n- **Class Variance Authority** - Type-safe component variants\r\n- **Lucide React** - Beautiful, customizable icons\r\n\r\n### Usage in Custom Blocks\r\n\r\n```typescript\r\nimport { Button, Card, Input, Label } from \"@databite/flow\";\r\n\r\nconst customFlow = createFlow(\"customFlow\")\r\n  .block(\r\n    \"customForm\",\r\n    async () => {\r\n      throw new Error(\"Should use onComplete\");\r\n    },\r\n    {\r\n      render: ({ onComplete, onError }) => (\r\n        <Card className=\"p-6\">\r\n          <div className=\"space-y-4\">\r\n            <div>\r\n              <Label htmlFor=\"name\">Name</Label>\r\n              <Input id=\"name\" placeholder=\"Enter your name\" />\r\n            </div>\r\n            <Button onClick={() => onComplete({ name: \"John\" })}>Submit</Button>\r\n          </div>\r\n        </Card>\r\n      ),\r\n      requiresInteraction: true,\r\n    }\r\n  )\r\n  .build();\r\n```\r\n\r\n## 💡 Usage Examples\r\n\r\n### Basic Interactive Flow\r\n\r\n```typescript\r\nimport { createFlow, FlowRenderer } from \"@databite/flow\";\r\nimport { z } from \"zod\";\r\n\r\n// Define return type schema\r\nconst UserSchema = z.object({\r\n  id: z.string(),\r\n  name: z.string(),\r\n  email: z.string(),\r\n  isActive: z.boolean(),\r\n});\r\n\r\n// Create an interactive flow\r\nconst userFlow = createFlow<UserSchema>(\"createUser\")\r\n  .form(\"getUserInfo\", {\r\n    title: \"User Information\",\r\n    description: \"Please provide your details\",\r\n    fields: [\r\n      { name: \"name\", label: \"Full Name\", required: true },\r\n      { name: \"email\", label: \"Email Address\", type: \"email\", required: true },\r\n    ],\r\n  })\r\n  .confirm(\"confirmDetails\", {\r\n    title: \"Confirm Details\",\r\n    message: (input) =>\r\n      `Create user: ${input.getUserInfo.name} (${input.getUserInfo.email})?`,\r\n  })\r\n  .http(\"createUser\", {\r\n    url: \"https://api.example.com/users\",\r\n    method: \"POST\",\r\n    returnType: { id: \"\", name: \"\", email: \"\", status: \"\" },\r\n    body: (input) => ({\r\n      name: input.getUserInfo.name,\r\n      email: input.getUserInfo.email,\r\n    }),\r\n  })\r\n  .transform(\"processUser\", (input) => ({\r\n    id: input.createUser.id,\r\n    name: input.createUser.name,\r\n    email: input.createUser.email,\r\n    isActive: input.createUser.status === \"active\",\r\n  }))\r\n  .display(\"showResult\", {\r\n    title: \"Success!\",\r\n    content: (input) =>\r\n      `User ${input.processUser.name} created with ID: ${input.processUser.id}`,\r\n  })\r\n  .returns((context) => context.processUser)\r\n  .build();\r\n\r\n// Use in React component\r\nfunction UserCreationPage() {\r\n  return (\r\n    <FlowRenderer\r\n      flow={userFlow}\r\n      onComplete={(result) => {\r\n        if (result.success) {\r\n          console.log(\"User created:\", result.data);\r\n        } else {\r\n          console.error(\"Flow failed:\", result.error);\r\n        }\r\n      }}\r\n    />\r\n  );\r\n}\r\n```\r\n\r\n**Key Features Demonstrated:**\r\n\r\n- **Type Safety**: Full TypeScript support with Zod schema validation\r\n- **Interactive Forms**: Built-in form handling with validation\r\n- **HTTP Requests**: Automatic error handling and timeout support\r\n- **Data Transformation**: Type-safe data processing between steps\r\n- **User Feedback**: Confirmation dialogs and result displays\r\n- **Return Transformation**: Proper return type handling\r\n\r\n### Data Processing Flow\r\n\r\n```typescript\r\nimport { createFlow } from \"@databite/flow\";\r\n\r\nconst dataProcessingFlow = createFlow(\"processData\")\r\n  .form(\"getConfig\", {\r\n    title: \"Processing Configuration\",\r\n    fields: [\r\n      { name: \"apiKey\", label: \"API Key\", type: \"password\", required: true },\r\n      {\r\n        name: \"baseUrl\",\r\n        label: \"Base URL\",\r\n        defaultValue: \"https://api.example.com\",\r\n      },\r\n    ],\r\n  })\r\n  .http(\"fetchRawData\", {\r\n    url: (input) => `${input.getConfig.baseUrl}/data/raw`,\r\n    method: \"GET\",\r\n    returnType: { items: [] },\r\n    headers: (input) => ({\r\n      Authorization: `Bearer ${input.getConfig.apiKey}`,\r\n    }),\r\n  })\r\n  .transform(\"validateData\", (input) => {\r\n    const validItems = input.fetchRawData.items.filter(\r\n      (item) => item.id && item.name && item.status\r\n    );\r\n\r\n    return {\r\n      validItems,\r\n      totalCount: input.fetchRawData.items.length,\r\n      validCount: validItems.length,\r\n      invalidCount: input.fetchRawData.items.length - validItems.length,\r\n    };\r\n  })\r\n  .display(\"showValidation\", {\r\n    title: \"Data Validation Results\",\r\n    content: (input) =>\r\n      `Found ${input.validateData.validCount} valid items out of ${input.validateData.totalCount} total items.`,\r\n  })\r\n  .confirm(\"proceedWithProcessing\", {\r\n    title: \"Continue Processing?\",\r\n    message: (input) => `Process ${input.validateData.validCount} valid items?`,\r\n  })\r\n  .transform(\"enrichData\", async (input) => {\r\n    if (!input.proceedWithProcessing) {\r\n      return { enrichedItems: [], processed: false };\r\n    }\r\n\r\n    // Simulate data enrichment\r\n    const enrichedItems = input.validateData.validItems.map((item) => ({\r\n      ...item,\r\n      enrichedAt: new Date().toISOString(),\r\n      category: \"default\",\r\n    }));\r\n\r\n    return { enrichedItems, processed: true };\r\n  })\r\n  .http(\"saveProcessedData\", {\r\n    url: (input) => `${input.getConfig.baseUrl}/data/processed`,\r\n    method: \"POST\",\r\n    returnType: { success: true, count: 0 },\r\n    headers: (input) => ({\r\n      Authorization: `Bearer ${input.getConfig.apiKey}`,\r\n      \"Content-Type\": \"application/json\",\r\n    }),\r\n    body: (input) => ({\r\n      items: input.enrichData.enrichedItems,\r\n      metadata: {\r\n        processedAt: new Date().toISOString(),\r\n        totalProcessed: input.enrichData.enrichedItems.length,\r\n      },\r\n    }),\r\n  })\r\n  .display(\"showCompletion\", {\r\n    title: \"Processing Complete!\",\r\n    content: (input) =>\r\n      `Successfully processed ${input.saveProcessedData.count} items.`,\r\n  })\r\n  .returns((context) => ({\r\n    processed: context.enrichData.processed,\r\n    itemCount: context.saveProcessedData.count,\r\n    timestamp: new Date().toISOString(),\r\n  }))\r\n  .build();\r\n```\r\n\r\n### Custom Hook Usage\r\n\r\n```typescript\r\nimport { createFlow, useFlowExecution } from \"@databite/flow\";\r\nimport { useState, useEffect } from \"react\";\r\n\r\nconst customFlow = createFlow(\"customFlow\")\r\n  .form(\"getInput\", {\r\n    fields: [{ name: \"value\", label: \"Enter a number\", type: \"number\" }],\r\n  })\r\n  .transform(\"calculate\", (input) => ({\r\n    result: input.getInput.value * 2,\r\n    original: input.getInput.value,\r\n  }))\r\n  .display(\"showResult\", {\r\n    content: (input) => `${input.original} * 2 = ${input.calculate.result}`,\r\n  })\r\n  .build();\r\n\r\nfunction CustomFlowComponent() {\r\n  const {\r\n    state,\r\n    currentBlock,\r\n    proceed,\r\n    reset,\r\n    getResult,\r\n    handleBlockComplete,\r\n    handleBlockError,\r\n  } = useFlowExecution(customFlow);\r\n\r\n  const [customMessage, setCustomMessage] = useState(\"\");\r\n\r\n  // Auto-proceed for non-interactive blocks\r\n  useEffect(() => {\r\n    if (!state.isExecuting && !state.isComplete) {\r\n      proceed();\r\n    }\r\n  }, [state.isExecuting, state.isComplete, proceed]);\r\n\r\n  // Custom handling for specific blocks\r\n  const handleCustomBlock = (blockName: string) => {\r\n    if (blockName === \"calculate\") {\r\n      // Add custom logic here\r\n      setCustomMessage(\"Calculation completed!\");\r\n    }\r\n  };\r\n\r\n  if (state.isComplete) {\r\n    const result = getResult();\r\n    return (\r\n      <div className=\"p-4 bg-green-50 border border-green-200 rounded\">\r\n        <h3 className=\"text-green-800 font-semibold\">Flow Complete!</h3>\r\n        <p className=\"text-green-600\">{customMessage}</p>\r\n        <pre className=\"mt-2 text-sm text-gray-700\">\r\n          {JSON.stringify(result.data, null, 2)}\r\n        </pre>\r\n        <button\r\n          onClick={reset}\r\n          className=\"mt-2 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700\"\r\n        >\r\n          Run Again\r\n        </button>\r\n      </div>\r\n    );\r\n  }\r\n\r\n  const [blockName, block] = currentBlock;\r\n\r\n  if (block.render) {\r\n    return (\r\n      <div>\r\n        <div className=\"mb-4 text-sm text-gray-600\">\r\n          Step {state.currentStepIndex + 1} of {state.totalSteps}: {blockName}\r\n        </div>\r\n        {block.render({\r\n          context: state.context,\r\n          onComplete: (data) => {\r\n            handleCustomBlock(blockName);\r\n            handleBlockComplete(data);\r\n          },\r\n          onError: handleBlockError,\r\n        })}\r\n      </div>\r\n    );\r\n  }\r\n\r\n  return (\r\n    <div className=\"p-4 bg-blue-50 border border-blue-200 rounded\">\r\n      <div className=\"flex items-center gap-3\">\r\n        <div className=\"animate-spin h-5 w-5 border-2 border-blue-600 border-t-transparent rounded-full\" />\r\n        <span className=\"text-blue-800\">\r\n          Executing: {blockName} (Step {state.currentStepIndex + 1} of{\" \"}\r\n          {state.totalSteps})\r\n        </span>\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n```\r\n\r\n**Custom Hook Features:**\r\n\r\n- **Manual Control**: Full control over flow execution\r\n- **Custom UI**: Build your own UI around flow state\r\n- **Block Handling**: Custom logic for specific blocks\r\n- **State Management**: Access to complete flow state\r\n- **Error Handling**: Custom error handling and recovery\r\n\r\n## 🔧 Advanced Usage\r\n\r\n### Custom Flow Blocks\r\n\r\nCreate your own flow blocks with custom UI components:\r\n\r\n```typescript\r\nimport { createFlow } from \"@databite/flow\";\r\nimport React from \"react\";\r\n\r\n// Custom file upload block\r\nconst fileUploadFlow = createFlow(\"fileUpload\")\r\n  .block(\r\n    \"uploadFile\",\r\n    async (input) => {\r\n      // This would be called programmatically\r\n      throw new Error(\r\n        \"File upload blocks should use onComplete() instead of run()\"\r\n      );\r\n    },\r\n    {\r\n      render: ({ onComplete, onError }) => {\r\n        const [file, setFile] = React.useState<File | null>(null);\r\n        const [uploading, setUploading] = React.useState(false);\r\n\r\n        const handleUpload = async () => {\r\n          if (!file) {\r\n            onError(\"Please select a file\");\r\n            return;\r\n          }\r\n\r\n          setUploading(true);\r\n          try {\r\n            const formData = new FormData();\r\n            formData.append(\"file\", file);\r\n\r\n            const response = await fetch(\"/api/upload\", {\r\n              method: \"POST\",\r\n              body: formData,\r\n            });\r\n\r\n            const result = await response.json();\r\n            onComplete(result);\r\n          } catch (error) {\r\n            onError(error.message);\r\n          } finally {\r\n            setUploading(false);\r\n          }\r\n        };\r\n\r\n        return (\r\n          <div className=\"max-w-md mx-auto p-6 bg-white border border-gray-200 rounded-lg\">\r\n            <h2 className=\"text-xl font-bold mb-4\">Upload File</h2>\r\n            <input\r\n              type=\"file\"\r\n              onChange={(e) => setFile(e.target.files?.[0] || null)}\r\n              className=\"mb-4 w-full px-3 py-2 border border-gray-300 rounded-md\"\r\n            />\r\n            <button\r\n              onClick={handleUpload}\r\n              disabled={!file || uploading}\r\n              className=\"w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50\"\r\n            >\r\n              {uploading ? \"Uploading...\" : \"Upload\"}\r\n            </button>\r\n          </div>\r\n        );\r\n      },\r\n      requiresInteraction: true,\r\n    }\r\n  )\r\n  .transform(\"processFile\", (input) => ({\r\n    fileName: input.uploadFile.fileName,\r\n    fileSize: input.uploadFile.fileSize,\r\n    uploadedAt: new Date().toISOString(),\r\n  }))\r\n  .build();\r\n```\r\n\r\n### Conditional Logic with User Interaction\r\n\r\n```typescript\r\nimport { createFlow } from \"@databite/flow\";\r\n\r\nconst conditionalFlow = createFlow(\"conditionalProcessing\")\r\n  .form(\"getUserChoice\", {\r\n    title: \"Processing Options\",\r\n    fields: [\r\n      {\r\n        name: \"processType\",\r\n        label: \"Processing Type\",\r\n        required: true,\r\n        defaultValue: \"standard\",\r\n      },\r\n    ],\r\n  })\r\n  .transform(\"checkRequirements\", (input) => {\r\n    const needsConfirmation = input.getUserChoice.processType === \"advanced\";\r\n\r\n    return {\r\n      processType: input.getUserChoice.processType,\r\n      needsConfirmation,\r\n      estimatedTime:\r\n        input.getUserChoice.processType === \"advanced\"\r\n          ? \"5 minutes\"\r\n          : \"2 minutes\",\r\n    };\r\n  })\r\n  .confirm(\"confirmAdvanced\", {\r\n    title: \"Advanced Processing\",\r\n    message: (input) =>\r\n      `Advanced processing will take ${input.checkRequirements.estimatedTime}. Continue?`,\r\n  })\r\n  .transform(\"executeProcessing\", (input) => {\r\n    if (\r\n      input.checkRequirements.processType === \"advanced\" &&\r\n      !input.confirmAdvanced\r\n    ) {\r\n      return { processed: false, reason: \"User cancelled advanced processing\" };\r\n    }\r\n\r\n    // Simulate processing based on type\r\n    const processingTime =\r\n      input.checkRequirements.processType === \"advanced\" ? 5000 : 2000;\r\n\r\n    return {\r\n      processed: true,\r\n      type: input.checkRequirements.processType,\r\n      duration: processingTime,\r\n    };\r\n  })\r\n  .display(\"showResults\", {\r\n    title: \"Processing Complete\",\r\n    content: (input) =>\r\n      `Successfully completed ${input.executeProcessing.type} processing in ${input.executeProcessing.duration}ms`,\r\n  })\r\n  .build();\r\n```\r\n\r\n### Error Handling and Recovery\r\n\r\n```typescript\r\nimport { createFlow } from \"@databite/flow\";\r\n\r\nconst robustFlow = createFlow(\"robustProcessing\")\r\n  .form(\"getConfig\", {\r\n    title: \"Configuration\",\r\n    fields: [\r\n      { name: \"apiKey\", label: \"API Key\", type: \"password\", required: true },\r\n      {\r\n        name: \"retryCount\",\r\n        label: \"Retry Count\",\r\n        type: \"number\",\r\n        defaultValue: 3,\r\n      },\r\n    ],\r\n  })\r\n  .transform(\"validateConfig\", (input) => {\r\n    if (!input.getConfig.apiKey || input.getConfig.apiKey.length < 10) {\r\n      throw new Error(\"Invalid API key provided\");\r\n    }\r\n\r\n    return {\r\n      apiKey: input.getConfig.apiKey,\r\n      retryCount: Math.max(1, Math.min(5, input.getConfig.retryCount || 3)),\r\n    };\r\n  })\r\n  .http(\"fetchData\", {\r\n    url: \"https://api.example.com/data\",\r\n    method: \"GET\",\r\n    returnType: { items: [] },\r\n    headers: (input) => ({\r\n      Authorization: `Bearer ${input.validateConfig.apiKey}`,\r\n    }),\r\n    timeout: 10000,\r\n  })\r\n  .transform(\"processData\", (input) => {\r\n    if (!input.fetchData.items || input.fetchData.items.length === 0) {\r\n      throw new Error(\"No data received from API\");\r\n    }\r\n\r\n    return {\r\n      processedItems: input.fetchData.items.map((item) => ({\r\n        ...item,\r\n        processedAt: new Date().toISOString(),\r\n      })),\r\n      totalCount: input.fetchData.items.length,\r\n    };\r\n  })\r\n  .display(\"showError\", {\r\n    title: \"Processing Error\",\r\n    content: (input) => `An error occurred: ${input.error || \"Unknown error\"}`,\r\n  })\r\n  .build();\r\n```\r\n\r\n## 🎨 Best Practices\r\n\r\n### 1. Use Descriptive Block Names\r\n\r\n```typescript\r\n// Good\r\n.form(\"getUserProfile\", { ... })\r\n.http(\"fetchUserData\", { ... })\r\n.transform(\"validateUserData\", (input) => { ... })\r\n.display(\"showUserInfo\", { ... })\r\n\r\n// Avoid\r\n.form(\"step1\", { ... })\r\n.http(\"fetch\", { ... })\r\n.transform(\"process\", (input) => { ... })\r\n.display(\"show\", { ... })\r\n```\r\n\r\n### 2. Design User-Friendly Forms\r\n\r\n```typescript\r\nconst userFriendlyFlow = createFlow(\"userFriendly\")\r\n  .form(\"getUserInfo\", {\r\n    title: \"Welcome! Let's get started\",\r\n    description: \"Please provide your information to continue\",\r\n    fields: [\r\n      {\r\n        name: \"firstName\",\r\n        label: \"First Name\",\r\n        placeholder: \"Enter your first name\",\r\n        required: true,\r\n      },\r\n      {\r\n        name: \"email\",\r\n        label: \"Email Address\",\r\n        type: \"email\",\r\n        placeholder: \"your.email@example.com\",\r\n        required: true,\r\n      },\r\n      {\r\n        name: \"phone\",\r\n        label: \"Phone Number\",\r\n        type: \"tel\",\r\n        placeholder: \"+1 (555) 123-4567\",\r\n      },\r\n    ],\r\n    submitLabel: \"Continue\",\r\n  })\r\n  .build();\r\n```\r\n\r\n### 3. Handle Errors Gracefully with User Feedback\r\n\r\n```typescript\r\nconst errorHandlingFlow = createFlow(\"errorHandling\")\r\n  .form(\"getConfig\", {\r\n    fields: [\r\n      { name: \"apiKey\", label: \"API Key\", type: \"password\", required: true },\r\n    ],\r\n  })\r\n  .http(\"fetchData\", {\r\n    url: \"https://api.example.com/data\",\r\n    returnType: { items: [] },\r\n    headers: (input) => ({ Authorization: `Bearer ${input.getConfig.apiKey}` }),\r\n  })\r\n  .transform(\"safeProcess\", (input) => {\r\n    try {\r\n      if (!input.fetchData.items || input.fetchData.items.length === 0) {\r\n        throw new Error(\"No data available\");\r\n      }\r\n\r\n      return {\r\n        result: input.fetchData.items.map((item) => processItem(item)),\r\n        error: null,\r\n      };\r\n    } catch (error) {\r\n      return {\r\n        result: null,\r\n        error: error.message,\r\n      };\r\n    }\r\n  })\r\n  .display(\"showError\", {\r\n    title: \"Processing Error\",\r\n    content: (input) =>\r\n      input.safeProcess.error\r\n        ? `Error: ${input.safeProcess.error}`\r\n        : `Successfully processed ${input.safeProcess.result.length} items`,\r\n  })\r\n  .build();\r\n```\r\n\r\n### 4. Use TypeScript for Better Type Safety\r\n\r\n```typescript\r\nimport { z } from \"zod\";\r\n\r\n// Define schemas for type safety\r\nconst UserSchema = z.object({\r\n  id: z.string(),\r\n  name: z.string(),\r\n  email: z.string(),\r\n  isActive: z.boolean(),\r\n});\r\n\r\nconst ProcessedUserSchema = UserSchema.extend({\r\n  lastSeen: z.string(),\r\n  processedAt: z.string(),\r\n});\r\n\r\nconst typedFlow = createFlow<typeof ProcessedUserSchema>(\"typedFlow\")\r\n  .form(\"getUserInput\", {\r\n    fields: [{ name: \"userId\", label: \"User ID\", required: true }],\r\n  })\r\n  .http(\"fetchUser\", {\r\n    url: (input) => `/users/${input.getUserInput.userId}`,\r\n    returnType: { id: \"\", name: \"\", email: \"\", status: \"\" },\r\n  })\r\n  .transform(\"processUser\", (input) => ({\r\n    id: input.fetchUser.id,\r\n    name: input.fetchUser.name,\r\n    email: input.fetchUser.email,\r\n    isActive: input.fetchUser.status === \"active\",\r\n    lastSeen: new Date().toISOString(),\r\n    processedAt: new Date().toISOString(),\r\n  }))\r\n  .returns((context) => context.processUser)\r\n  .build();\r\n```\r\n\r\n### 5. Provide Clear User Feedback\r\n\r\n```typescript\r\nconst feedbackFlow = createFlow(\"feedbackFlow\")\r\n  .display(\"welcome\", {\r\n    title: \"Welcome to Data Processing\",\r\n    content: \"This flow will help you process your data step by step.\",\r\n  })\r\n  .form(\"getData\", {\r\n    title: \"Data Configuration\",\r\n    description: \"Please provide the necessary information\",\r\n    fields: [\r\n      { name: \"dataSource\", label: \"Data Source\", required: true },\r\n      { name: \"processingType\", label: \"Processing Type\", required: true },\r\n    ],\r\n  })\r\n  .display(\"processing\", {\r\n    title: \"Processing Your Data\",\r\n    content: (input) =>\r\n      `Processing ${input.getData.dataSource} using ${input.getData.processingType} method...`,\r\n  })\r\n  .confirm(\"confirmResults\", {\r\n    title: \"Review Results\",\r\n    message: (input) =>\r\n      `Data processing completed. The results are ready for review. Continue?`,\r\n  })\r\n  .display(\"success\", {\r\n    title: \"Success!\",\r\n    content:\r\n      \"Your data has been processed successfully. You can now download the results.\",\r\n  })\r\n  .build();\r\n```\r\n\r\n## 🧪 Testing\r\n\r\n### Unit Testing Flows\r\n\r\n```typescript\r\nimport { createFlow } from \"@databite/flow\";\r\nimport { render, screen, fireEvent, waitFor } from \"@testing-library/react\";\r\nimport { FlowRenderer } from \"@databite/flow\";\r\n\r\nconst testFlow = createFlow(\"testFlow\")\r\n  .form(\"getInput\", {\r\n    fields: [{ name: \"value\", label: \"Value\", type: \"number\" }],\r\n  })\r\n  .transform(\"addOne\", (input) => input.getInput.value + 1)\r\n  .transform(\"multiplyByTwo\", (input) => input.addOne * 2)\r\n  .display(\"showResult\", {\r\n    content: (input) => `Result: ${input.multiplyByTwo}`,\r\n  })\r\n  .build();\r\n\r\n// Test flow execution\r\ntest(\"flow processes input correctly\", async () => {\r\n  render(<FlowRenderer flow={testFlow} />);\r\n\r\n  // Fill form\r\n  const input = screen.getByLabelText(\"Value\");\r\n  fireEvent.change(input, { target: { value: \"5\" } });\r\n\r\n  // Submit form\r\n  const submitButton = screen.getByText(\"Continue\");\r\n  fireEvent.click(submitButton);\r\n\r\n  // Wait for result\r\n  await waitFor(() => {\r\n    expect(screen.getByText(\"Result: 12\")).toBeInTheDocument(); // (5 + 1) * 2\r\n  });\r\n});\r\n```\r\n\r\n### Testing Custom Blocks\r\n\r\n```typescript\r\nimport { createFlow } from \"@databite/flow\";\r\nimport { render, screen, fireEvent } from \"@testing-library/react\";\r\n\r\nconst customFlow = createFlow(\"customFlow\")\r\n  .block(\r\n    \"customBlock\",\r\n    async () => {\r\n      throw new Error(\"Should use onComplete\");\r\n    },\r\n    {\r\n      render: ({ onComplete, onError }) => (\r\n        <div>\r\n          <button onClick={() => onComplete({ test: \"data\" })}>Complete</button>\r\n          <button onClick={() => onError(\"Test error\")}>Error</button>\r\n        </div>\r\n      ),\r\n      requiresInteraction: true,\r\n    }\r\n  )\r\n  .build();\r\n\r\ntest(\"custom block handles completion\", async () => {\r\n  render(<FlowRenderer flow={customFlow} />);\r\n\r\n  const completeButton = screen.getByText(\"Complete\");\r\n  fireEvent.click(completeButton);\r\n\r\n  await waitFor(() => {\r\n    expect(screen.getByText(/Flow Complete/)).toBeInTheDocument();\r\n  });\r\n});\r\n```\r\n\r\n## 🔗 Related Packages\r\n\r\n- [@databite/build](./packages/build/) - Core connector builder SDK\r\n- [@databite/types](./packages/types/) - Shared TypeScript types\r\n- [@databite/connectors](./packages/connectors/) - Pre-built connector library\r\n- [@databite/connect](./packages/connect/) - Connection management and authentication\r\n- [@databite/engine](./packages/engine/) - Data synchronization and execution engine\r\n\r\n## 📄 License\r\n\r\nMIT License - see [LICENSE](../../LICENSE) for details.\r\n\r\n## 🤝 Contributing\r\n\r\nWe welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for details.\r\n\r\n## 📞 Support\r\n\r\n- [GitHub Issues](https://github.com/DatabiteDev/databite/issues) - Bug reports and feature requests\r\n- [Discussions](https://github.com/DatabiteDev/databite/discussions) - Community discussions and questions\r\n- [Documentation](https://docs.databite.dev) - Full documentation and guides\r\n","readmeFilename":"README.md"}