# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Development
yarn start                  # Dev server with hot-reload (ts-node + nodemon)

# Build
yarn build                  # Full production build (clean + tsc + copy schema)
yarn build:server           # Compile TypeScript only

# Testing
yarn test                   # Full suite: format check + lint + unit tests
yarn test:unit              # Jest unit tests only
yarn test:unit:watch        # Watch mode
yarn test:integration       # Integration tests (requires running server)
yarn test:types             # TypeScript type checking (no emit)

# Linting & Formatting
yarn lint                   # ESLint + GraphQL schema linting
yarn lint:fix               # Auto-fix lint issues
yarn format                 # Prettier formatting
yarn format:check           # Check formatting only

# Code Generation
yarn generate:types         # Generate TypeScript types from GraphQL schema (codegen.yml)
```

To run a single test file:

```bash
yarn test:unit -- --testPathPattern="path/to/test"
```

## Setup

Copy `.env.shadow` to `.env` before running locally: `cp .env.shadow .env`

## Architecture

This is an **Apollo GraphQL server** (TypeScript, Node >=22.17.1) that aggregates analytics data for a music distribution platform. It proxies and composes data from five internal OWS (Orchard Web Services) microservices.

### Data Flow

```
GraphQL Query → Resolver → DataLoader (batching + cache) → DataSource → OWS Microservice API
                                        ↑
                                   Redis / in-memory cache (default TTL: 5min)
```

### Key Directories

-   **`src/schema/`** — Modular `.graphql` files organized by domain. `Query.graphql` and `schema.graphql` are entry points. Codegen glob `src/schema/[^_]*.graphql` skips underscore-prefixed files.
-   **`src/resolvers/`** — Field resolvers plus subdirectories: `types/` (mapper Key interfaces), `input/` (Zod input validators), `enums/` (TypeScript enum values mapped in codegen).
-   **`src/connectors/`** — One DataSource per OWS backend: `ows-analytics`, `ows-charts`, `ows-playlist`, `ows-socials`, `ows-users`. Each follows the same structure: `dataloaders/`, `formatters/`, `types/`, `__tests__/`.
-   **`src/generated/index.ts`** — Auto-generated resolver types. Do not edit manually.
-   **`src/constants/`** — URLs, cache TTL constants (`ONE_DAY`, `ONE_HOUR`), feature flag names, distributor definitions.
-   **`tests/integration/`** — Integration tests with `.graphql` query files in `queries/` and specs in `specs/`.
-   **`lib/test-helpers/`** — Shared test utilities and factory builders.

### Path Aliases

Defined in `tsconfig.json` and mirrored in Jest `moduleNameMapper`:

-   `src/*` → `./src/*`
-   `lib/*` → `./lib/*`
-   `tests/*` → `./tests/*`

Use these aliases in all imports (e.g., `import { ServiceContext } from 'src/types'`).

### ServiceContext

Every resolver receives a `ServiceContext` (defined in `src/types.ts`) containing:

-   `dataSources` — instances of all five OWS DataSource connectors (`owsCharts`, `owsSocials`, `owsAnalytics`, `owsUsers`, `owsPlaylist`)
-   `identity?` — authenticated user identity

### Key Patterns

1. **Zod validation** — All external API responses are validated with Zod schemas in `connectors/*/types/`. Schemas use `.transform()` to map API snake_case to camelCase. Always add/update Zod schemas when changing what a connector fetches.

2. **DataLoader factory pattern** — Each dataloader is a module exporting: `create<Name>DataLoader(post)` factory, `<Name>DataLoader` type alias, `dataSchema` (Zod schema), and `cacheKeyFn` (format: `TypeName:${key}`). Use `ZodDataLoader` for standard caching or `RedisZodDataLoader` when Redis-backed distributed caching is needed.

3. **Resolver type conventions** — Query resolvers use `satisfies Partial<QueryResolvers>`. Object type resolvers use `satisfies <TypeName>Resolvers`. Import resolver types from `src/generated`.

4. **Mapper keys** — Every GraphQL object type has a corresponding `*Key` interface in `src/resolvers/types/` declared in `codegen.yml` `mappers:`. This is the parent value type that resolvers receive. When adding a new GraphQL type, create a Key interface and register it in `codegen.yml`.

5. **Enum mapping** — GraphQL enums map to TypeScript values via `codegen.yml` `enumValues:`. Enums are generated as `as const` objects. Enum values live in `src/resolvers/enums/`.

6. **Type generation** — After modifying `.graphql` files, run `yarn generate:types` to regenerate `src/generated/index.ts`. Also regenerates `tests/integration/definitions/index.ts` for integration test query types.

7. **Feature flags** — Split.io feature flags are referenced via constants in `src/constants/features.ts` and accessed through `src/featuresConfig.ts`.

8. **Caching** — Cache control hints in `src/schema/caching.graphql`. Default max age is 5 minutes. Set `ttl: 0` explicitly when results are user-dependent or feature-flag-dependent. Use TTL constants from `src/constants/cache.ts`.

### Testing Patterns

-   **Factory builders** — `factoryBuilder<T>(defaultValues, requiredKeys)` from `lib/test-helpers/factory-builder.ts` returns `(overrides?) => T`. Per-connector response factories live in `lib/test-helpers/factories/connectors/`.
-   **Mocked data sources** — `MockedDataSources` (from `lib/test-helpers/`) provides `jest.Mocked` versions of all connectors. `ContextFactory` builds mock `ServiceContext`.
-   **DataLoader tests** — Test Zod schema validation (valid + invalid), `cacheKeyFn` output, DataLoader instantiation, and batch function results. Mock `PostFn` with `jest.fn()`.
-   **Unit vs integration** — Unit tests live in `src/**/​__tests__/`. Integration tests live in `tests/integration/` and hit a real running server.
