# CLAUDE.md

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

## What This Is

`@unito/integration-sdk` — a TypeScript framework for building REST API integrations compatible with the Unito Integration API Specification. It wraps Express 5 and provides handler routing, middleware chains for request parsing, structured logging, a Provider HTTP client for downstream APIs, and standardized error handling.

## Commands

```bash
npm install              # Install deps (also runs compile via prepare hook)
npm run compile          # Build ESM (tsc), copy non-TS assets, then CJS (rollup)
npm run compile:watch    # Watch mode recompile
npm test                 # Run all tests
ONLY=Handler npm test    # Run tests matching pattern "Handler"
npm run test:debug       # Run tests with --inspect-brk
npm run lint             # ESLint --fix + Prettier --write on src/ and test/
```

Tests use **Node.js built-in test runner** (`node:test` + `node:assert/strict`) with `ts-node/esm` loader — no compilation needed to run tests. HTTP mocking uses `nock`.

## Architecture

### Request Flow

```
HTTP Request
  → finish (error/log hooks on response end)
  → express.json (1MB limit)
  → start (records hrtime)
  → correlationId (generates/extracts X-Correlation-Id)
  → logger (structured logger decorated with correlation ID)
  → credentials (decodes base64 JSON from X-Unito-Credentials header)
  → secrets (decodes base64 JSON from X-Unito-Secrets header)
  → filters / search / selects / relations (parse query params)
  → signal (AbortSignal from X-Unito-Operation-Deadline timestamp)
  → Handler routes (user-registered)
  → errors middleware (catches, formats HttpError responses)
  → notFound middleware (404 fallback)
```

### Core Classes

**Integration** (`src/integration.ts`) — entry point. Call `addHandler(path, handlers)` to register routes, then `start()`. All handlers must be added before `start()` — the server exits on violation.

**Handler** (`src/handler.ts`) — takes a path and a handlers object, generates an Express Router. Path determines routing:
- `/things/:thingId` — last segment is `:variable` → item route. `getItem` at `GET /things/:thingId`, `getCollection` at `GET /things`, `createItem` at `POST /things`, etc.
- `/things` — no trailing variable → either item-level or collection-level ops, but not both on the same path.

Handler types (pick one per `addHandler` call):
- `ItemHandlers` — getItem, getCollection, createItem, updateItem, deleteItem
- `BlobHandlers` — getBlob, createBlob
- `CredentialAccountHandlers` — getCredentialAccount
- `ParseWebhookHandlers`, `WebhookSubscriptionHandlers`, `AcknowledgeWebhookHandlers`

**Provider** (`src/resources/provider.ts`) — HTTP client for calling downstream provider APIs. Initialized with `prepareRequest` (returns base URL + auth headers), optional `rateLimiter`, and optional `customErrorHandler`. Methods: `get`, `post`, `put`, `patch`, `delete`, `streamingGet`, `postForm`, `postStream`, `putBuffer`.

**Context** (`src/resources/context.ts`) — typed context object passed to every handler. Contains `credentials`, `secrets`, `logger`, `signal`, `params`, `query`. GetCollectionContext additionally has `filters`, `search`, `selects`, `relations`. Create/Update contexts have `body`.

**HttpErrors** (`src/httpErrors.ts`) — throw these from handlers for proper error responses. BadRequestError(400), UnauthorizedError(401), ForbiddenError(403), NotFoundError(404), TimeoutError(408), UnprocessableEntityError(422), ProviderInstanceLockedError(423), RateLimitExceededError(429). The latter two support `retryAfter`.

### Logging

Logger (`src/resources/logger.ts`) produces Datadog-compatible structured JSON in production, colored output in dev. Keys are auto-converted to snake_case. Sensitive fields (access_token, password, email, etc.) are auto-redacted. Log size truncated at 20KB (configurable via `MAX_LOG_MESSAGE_SIZE` env var).

### Cache

`Cache.create(redisUrl?)` returns a Redis-backed or local in-memory cache via the `cachette` library.

## Code Conventions

- **ES Modules** — `"type": "module"` in package.json. All internal imports use `.js` extensions (`import X from './foo.js'`).
- **Dual output** — ESM via `tsc`, CJS via Rollup. Published with conditional exports.
- **Strict TypeScript** — strict mode, noUncheckedIndexedAccess, exactOptionalPropertyTypes, no unused locals/params.
- **Branded types** — e.g. `type Path = string & { __brand: 'Path' }` with assertion functions (`asserts path is Path`).
- **No barrel files** — import directly from source modules, not from index re-exports.
- **Prettier** — single quotes, trailing commas, 120 char width, no parens on single-param arrows.
- **Test files** — `test/**/*.test.ts` mirroring src structure. Use `describe`/`it`/`beforeEach`/`afterEach` from `node:test`, assertions from `node:assert/strict`, mocking with `mock.method()`.
- **Express typing** — `res.locals` is typed via global Express namespace augmentation.
