This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- Node.js >= 20, npm ^10 (defined in
package.json) - Estuary installed and running locally
- GitHub authentication token in
~/.npmrcfor@estuary/flow-webpackage://npm.pkg.github.com/:_authToken=YOUR_TOKEN @estuary:registry=https://npm.pkg.github.com/
npm install # Install dependencies
npm start # Start dev server at localhost:3000 (runs licenses check first)npm run lint # Run ESLint
npm run lint:fix # Auto-fix ESLint issues
npm run format # Check Prettier formatting
npm run format:fix # Auto-fix formatting
npm run typecheck # Run TypeScript compiler checks
npm run licenses # Check dependency licensesnpm test # Run Vitest in watch mode
npm run test:ui # Run Vitest with UI
npm run test:debug # Debug tests with inspectornpm run generate-flow-types # Generate types from estuary/flow (requires Flow installed)npm run hack-in-web-flow # Use to pull latest @estuary/flow-web build
npm run hack-in-local-web-flow # Use local @estuary/flow-web build
npm run hack-in-dpg # Use local data-plane-gateway buildThree layers: React Context (top-level concerns), Zustand stores (domain state), and SWR (REST fetching). Stores are composed from slices and accessed via pre-made hooks (moving away from inline useShallow selectors as of Q1 2026).
See docs/STATE.md for store composition, hydration patterns, workflow state machines, and binding index tracking.
See docs/REACT.md for React-specific patterns (context, hooks, code splitting, error boundaries).
- GraphQL (URQL) — preferred for all new data fetching; real-time alerts, live specs, shard status
- PostgREST (Supabase) — batch operations (drafts, publications, entity lists); wrapped in
src/api/ - WebAssembly (
@estuary/flow-web) — client-side validation:evaluate_field_selection(),update_materialization_resource_spec(),get_resource_config_pointers()
See docs/GRAPHQL.md for migration status and URQL patterns.
Supabase auth with JWT refresh, SSO, magic link, and access grant token support. Auth state flows from Supabase → UserStore (Zustand) → RequireAuth route guards.
See docs/AUTH.md for the full auth flow, provider nesting order, and JWT refresh strategy.
Routes defined in src/app/routes.ts with two top-level objects:
authenticatedRoutes: Protected routes wrapped byRequireAuthandAuthenticatedLayoutunauthenticatedRoutes: Login, registration, SSO flows
All lazy-loaded routes and <Suspense> boundaries are centralized in src/context/Router/index.tsx.
See docs/ROUTING.md for the full route structure, guards, and URL param conventions.
Custom renderer pipeline for connector configuration forms. Takes a connector JSON schema, generates a UI schema, applies custom renderers (OAuth, Duration, Discriminator), and binds to Zustand store data.
See docs/JSONFORMS.md for the full pipeline, custom annotations, renderers, and AJV validation setup.
MUI v7 with a custom theme in src/context/Theme.tsx. Prefer exported theme tokens over hardcoded values. Use slots/slotProps (not components/componentsProps). Prefer iconoir-react over @mui/icons-material.
See docs/MUI.md for theme tokens, conventions, and known gotchas.
react-intl with English-only messages in src/lang/en-US/. Missing keys are logged to LogRocket.
See docs/I18N.md for message file structure, usage patterns, and naming conventions.
Four layers: React error boundaries, store hydration errors, form validation (AJV), and API call errors. All error display flows through <AlertBox> and logs to LogRocket.
See docs/ERROR_HANDLING.md for the full error flow and display components.
Supabase (auth + database), GraphQL server, Data-Plane-Gateway, Monaco Editor, LogRocket.
See docs/INTEGRATIONS.md for setup details and critical initialization order requirements.
Vite with plugins for ESLint/TS checking, circular dependency detection, WASM support, and SRI hashing.
See docs/BUILD.md for plugin details.
src/api/: Supabase REST query wrappers (entities, drafts, publications, connectors)src/components/: UI components organized by feature (capture, materialization, collection, tables, shared)src/context/: React Context providers (User, Router, SWR, URQL, Theme, Notifications)src/forms/: JSON Forms custom renderers and overridessrc/hooks/: Custom hooks organized by domain (connectors, bindings, journals, searchParams)src/services/: Shared logic and wrappers for external dependencies (AJV setup, Supabase utilities, UI schema generation, GraphQL helpers)src/stores/: Zustand stores (Binding, Entities, Workflow, FormState, JournalData)src/types/: TypeScript types (GraphQL, JSON Forms, WASM, schema models)src/utils/: Shared logic in basic functions organized by target (billing, schema, workflow)
See docs/AUTH.md for the full nesting order. Critical rules:
- LogRocket initializes before Supabase client creation
UrqlConfigProvidermust be insideUserStoreProvider- Monaco workers are configured in
src/index.tsxbefore React renders
- Encrypted fields break discriminator matching: Use custom logic in
src/forms/shared.tsthat matches by discriminator value alone — seedocs/JSONFORMS.md - Store active flag: Set
store.setActive(false)on unmount to prevent unnecessary hydration. Eventually this should be replaced by cleaning up / cancelling ongoing calls during unmount. - LogRocket before Supabase: LogRocket must initialize first to capture Supabase network requests — see
docs/INTEGRATIONS.md - Monaco workers: Must be configured before React renders (done in
src/index.tsx) — seedocs/INTEGRATIONS.md - Data plane token refresh: Check
shouldRefreshToken()on errors and re-authenticate before retry — seedocs/INTEGRATIONS.md - Trial collections: Cannot be deleted, have special handling in
useTrialCollections()hook - Binding UUID tracking: Use correct index mapping when cross-referencing errors between client/server state — see
docs/STATE.md
See docs/TESTING.md for detailed test scenarios (connector edge cases, create/edit workflows, binding variations).
- Unit tests: Vitest + jsdom, located at
src/**/__tests__/*.test.ts - E2E tests: Playwright, located in
playwright-tests/(intended to become a standalone project)
Currently migrating from PostgREST to GraphQL for select features. See docs/GRAPHQL.md for details and current status.
- GraphQL cache disabled for real-time types: Alerts/LiveSpecRef return
nullfor cache key to always fetch fresh for now — seedocs/GRAPHQL.md
This list is a work in progress and not complete by any means. Some of this is called out in the docs.
- Moving to strict state machines
- Replacing PostgREST to GQL
- Moving broken up Zustand states into a single one or two stores