{"id":50547341,"url":"https://github.com/osbytes/epoch","last_synced_at":"2026-06-04T00:01:34.099Z","repository":{"id":355533452,"uuid":"1228159740","full_name":"osbytes/epoch","owner":"osbytes","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-04T03:47:13.000Z","size":154,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-04T05:28:13.441Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/osbytes.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-03T17:11:42.000Z","updated_at":"2026-05-04T03:47:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/osbytes/epoch","commit_stats":null,"previous_names":["osbytes/epoch"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/osbytes/epoch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osbytes%2Fepoch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osbytes%2Fepoch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osbytes%2Fepoch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osbytes%2Fepoch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/osbytes","download_url":"https://codeload.github.com/osbytes/epoch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osbytes%2Fepoch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33884734,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-04T00:01:33.420Z","updated_at":"2026-06-04T00:01:34.084Z","avatar_url":"https://github.com/osbytes.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Epoch Flow\n\n\u003cp align=\"center\"\u003e\n  \u003cimg\n    src=\"docs/public/logo.svg\"\n    alt=\"Epoch Flow — multi-step form flow\"\n    width=\"160\"\n    height=\"160\"\n  /\u003e\n\u003c/p\u003e\n\n\u003e Draft-aware, end-to-end typed multi-step form flows for React + tRPC + Zod.\n\n\u003c!-- Org set to osbytes --\u003e\n[![CI](https://github.com/osbytes/epoch/actions/workflows/ci.yml/badge.svg)](https://github.com/osbytes/epoch/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@epochflow/core)](https://www.npmjs.com/package/@epochflow/core)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nEpoch Flow is a thin, headless workflow orchestrator that composes **Zod** and **tRPC** into typed, persistent, multi-step form flows — without replacing either of them.\n\n## Features\n\n- **Step-based navigation** — Linear `next` / `back` / `goTo` with built-in guards\n- **Per-step Zod validation** — Only validate the fields visible on the current step\n- **Draft persistence** — Auto-save to `localStorage` with debounce; restore on return\n- **Typed tRPC mutations** — Submit flows directly to your tRPC router with full inference\n- **Headless by design** — Bring your own UI; zero styling opinions\n- **Framework-agnostic core** — Vanilla JS state machine + store; React bindings in a separate package\n- **Lightweight** — ~9 KB total (gzipped) including React bindings\n\n## Install\n\n```bash\nnpm install @epochflow/react zod react react-dom\n# or\npnpm add @epochflow/react zod react react-dom\n```\n\n\u003e `@epochflow/core` is included automatically as a dependency of `@epochflow/react`.\n\nIf you plan to submit data via tRPC, also install your tRPC client packages:\n\n```bash\nnpm install @trpc/client @trpc/server\n```\n\n## Compatibility\n\n| Package         | Supported versions |\n|-----------------|--------------------|\n| React           | `^18.0.0 \\| ^19.0.0` |\n| React DOM       | `^18.0.0 \\| ^19.0.0` |\n| Zod             | `^3.22.0`          |\n| tRPC Client     | `^10.0.0 \\| ^11.0.0` (optional) |\n| tRPC Server     | `^10.0.0 \\| ^11.0.0` (optional) |\n\n\u003e **React Server Components (RSC):** Epoch Flow is currently client-only. It works inside `\"use client\"` boundaries in Next.js App Router.\n\n## 30-Second Example\n\n```tsx\nimport { z } from 'zod'\nimport { createFormFlow } from '@epochflow/core'\nimport { FormFlowProvider, useFormFlow } from '@epochflow/react'\n\nconst LeadSchema = z.object({\n  firstName: z.string().min(1),\n  lastName: z.string().min(1),\n  email: z.string().email(),\n})\n\ntype LeadData = z.infer\u003ctypeof LeadSchema\u003e\n\nconst flow = createFormFlow({\n  schema: LeadSchema,\n  steps: {\n    personal: ['firstName', 'lastName'],\n    contact: ['email'],\n  },\n  persist: { key: 'lead-draft', debounceMs: 1000 },\n  mutation: async (data) =\u003e {\n    const response = await fetch('/api/leads', {\n      method: 'POST',\n      body: JSON.stringify(data),\n    })\n    return response.json()\n  },\n})\n\nfunction PersonalStep() {\n  const { values, errors, setValues, next, validateCurrentStep } =\n    useFormFlow\u003cLeadData\u003e()\n\n  function handleNext() {\n    if (validateCurrentStep() === null) next()\n  }\n\n  return (\n    \u003c\u003e\n      \u003cinput\n        value={values.firstName ?? ''}\n        onChange={(event) =\u003e setValues({ firstName: event.target.value })}\n      /\u003e\n      {errors.firstName \u0026\u0026 \u003cspan\u003e{errors.firstName}\u003c/span\u003e}\n      \u003cbutton onClick={handleNext}\u003eNext\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n\nfunction App() {\n  return (\n    \u003cFormFlowProvider flow={flow}\u003e\n      \u003cPersonalStep /\u003e\n    \u003c/FormFlowProvider\u003e\n  )\n}\n```\n\n## What about React Hook Form?\n\nEpoch Flow provides its own lightweight controlled-state form handling (`values` / `setValues`). You can use it **alongside** React Hook Form if you prefer — for example, using RHF inside individual steps for advanced field-level validation — but **React Hook Form is not required**.\n\n## Packages\n\n| Package | Purpose | Install |\n|---------|---------|---------|\n| `@epochflow/core` | Framework-agnostic state machine, store, validation, persistence | Included with `@epochflow/react` |\n| `@epochflow/react` | React provider, hooks (`useFormFlow`, `useStepFields`, `usePersistedDraft`) | `npm i @epochflow/react` |\n\n## Compared to...\n\n| Tool | What it does | How Epoch Flow differs |\n|------|-------------|---------------------|\n| **React Hook Form** | Performant form validation with uncontrolled inputs | Epoch Flow focuses on multi-step orchestration, persistence, and tRPC submission — not field-level performance. Use both together if you like. |\n| **[Wizzard](https://github.com/ZizzX/wizzard-packages)** | Modular headless wizard engine with multiple framework bindings | Epoch Flow is smaller and more opinionated: one factory (`createFormFlow`), one validation strategy (Zod), and first-class tRPC typing. |\n| **Rolling your own** | `useReducer` + `zodResolver` + `localStorage` | Epoch Flow saves you from writing and maintaining the same glue code in every project. |\n\n## Demo\n\nA polished SaaS onboarding wizard lives in `apps/demo/`. It showcases:\n\n- Step transitions with animations\n- Draft persistence (refresh the page, data returns)\n- Per-step validation\n- Typed tRPC mutation (mocked backend)\n- Mobile-responsive layout\n\n```bash\ncd apps/demo\npnpm dev\n```\n\n## Documentation\n\n- **Docs site** — Run locally with `pnpm docs:dev`. When published to GitHub Pages from this repo, the site is served under the `/epoch/` base path (see `docs/.vitepress/config.mts`).\n- [Getting Started](./docs/getting-started.md) — Step-by-step tutorial\n- [API Reference](./docs/api-reference.md) — Complete API documentation\n- [Examples](./docs/examples.md) — Common patterns and recipes\n- [Architecture](./docs/architecture.md) — How it works under the hood\n- [Contributing](./CONTRIBUTING.md) — Development workflow\n- [Changelog](./CHANGELOG.md) — Release history (per-package via Changesets)\n- [Security](./SECURITY.md) — Vulnerability disclosure\n- [Code of Conduct](./CODE_OF_CONDUCT.md) — Community guidelines\n\n## License\n\nMIT © Epoch Flow Contributors","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosbytes%2Fepoch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fosbytes%2Fepoch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosbytes%2Fepoch/lists"}