Files
cfw-autumn/.claude/rules/sdk.mdc
2026-03-23 16:56:43 +00:00

218 lines
7.2 KiB
Plaintext

---
alwaysApply: true
---
# SDK Generation Pipeline
Zod Schemas → ORPC Contracts → OpenAPI Spec → Speakeasy SDKs → autumn-js Wrapper → React Hooks
Webhook Zod Schemas → Webhook Registry → OpenAPI Spec (injected) + Svix Event Types
Run `bun api` from root to execute the full pipeline.
---
## 1. Zod Schema Layer
**Location:** `shared/api/`
- Use `.describe()` for field descriptions in API docs
- Use `.meta({ internal: true })` for fields stripped from public OpenAPI spec
- Use `.meta({ id: "SchemaName" })` for schema registration
---
## 2. ORPC Contract Layer
**Location:** `packages/openapi/v2.1/contracts/`
Each contract defines: `method`, `path`, `operationId`, `tags`, `input`, `output`.
All contracts aggregated in `packages/openapi/v2.1/contracts/index.ts`.
---
## 3. OpenAPI Generation
**Entry point:** `packages/openapi/v2.1/openapi2.1.ts`
Steps: Register internal schemas → Generate via ORPC → Apply Speakeasy settings → Inject global headers → Remove internal fields.
The generated SDK/OpenAPI `x-api-version` default must come from `LATEST_VERSION` in `shared/api/versionUtils/ApiVersion.ts` and currently resolves to `2.2.0`.
---
## 4. SDK Generation
**Entry point:** `packages/openapi/api.ts`
- **TypeScript:** `bunx speakeasy run -t autumn` → `packages/sdk/`
- **Python:** `bunx speakeasy run -t autumn-python` → `others/python-sdk/`
---
## 5. autumn-js Architecture
**Location:** `packages/autumn-js/src/`
### Directory Structure
```
packages/autumn-js/src/
├── backend/ # Server-side
│ ├── adapters/ # Framework adapters (hono.ts, next.ts)
│ └── core/
│ ├── handlers/ # Request processing
│ │ ├── coreHandler.ts # Main handler factory
│ │ ├── executeRoute.ts # Route execution logic
│ │ └── resolveIdentity.ts
│ ├── routes/
│ │ ├── defaultRoutes.ts # Route definitions
│ │ └── routeBuilder.ts # rou3-based router
│ ├── types/ # Type definitions
│ └── utils/ # backendRes, secretKeyCheck
├── react/ # Client-side
│ ├── client/
│ │ ├── AutumnClient.ts # Client factory
│ │ └── internal/httpClient.ts
│ ├── hooks/ # useCustomer, useListPlans
│ └── AutumnContext.tsx # React context provider
├── sdk/ # Re-exports from @useautumn/sdk
└── utils/ # Shared utilities
```
### Backend: Route Definitions
Routes defined in `defaultRoutes.ts` using `RouteDefinition`:
```typescript
type RouteDefinition = {
route: string; // RPC-style name: "customers.get_or_create"
sdkMethod: (autumn, args) => Promise<any>;
inject?: { customerId?: boolean; customerData?: boolean };
customHandler?: CustomHandlerFn; // For special logic
requireCustomer?: boolean; // Default: true
};
```
Standard routes use `inject` to auto-add identity fields. Use `customHandler` for complex logic (e.g., `customers.get_or_create` handles `errorOnNotFound`).
### Backend: Framework Adapters
Adapters in `backend/adapters/` convert framework requests to `UnifiedRequest`:
```typescript
type UnifiedRequest = {
method: string;
path: string;
body: unknown;
raw: unknown; // Original request for identify()
};
```
### React Client
`AutumnClient.ts` creates a client with namespaced methods that call backend routes:
```typescript
const client = createAutumnClient({ baseUrl: "/api/autumn" });
// client.customers.getOrCreate(), client.billing.attach(), client.plans.list()
```
### React Hooks
Hooks use TanStack Query with consistent patterns:
```typescript
export const useCustomer = (params: UseCustomerParams = {}) => {
const client = useAutumnClient();
const { errorOnNotFound, queryOptions, ...sdkParams } = params;
return useQuery<Customer | null, ClientError>({
queryKey: ["autumn", "customer", sdkParams],
queryFn: async () => {
const response = await client.customers.getOrCreate({...});
if (response.error) throw response.error;
return response.data;
},
...queryOptions,
});
};
```
### SDK Casing Convention
**Important:** The TypeScript SDK and React hooks use **camelCase** for all properties. Speakeasy automatically transforms snake_case API responses to camelCase.
- API response: `{ plan_id: "pro", created_at: 123, auto_enable: true }`
- SDK/Hook response: `{ planId: "pro", createdAt: 123, autoEnable: true }`
When writing documentation examples for React hooks or the TypeScript SDK, always use camelCase:
- `data?.balances?.messages?.featureId` (not `feature_id`)
- `subscription.planId` (not `plan_id`)
- `plan.createdAt` (not `created_at`)
---
## 6. Webhook Pipeline
**Registry:** `shared/api/webhooks/webhookRegistry.ts`
Each webhook is defined as a `WebhookDefinition` with `eventType`, `operationId`, `title`, `schema` (Zod), `description`, and `group`. Schemas live in `shared/api/webhooks/`.
### OpenAPI injection
`packages/openapi/v2.1/webhooks/injectWebhooks.ts` reads the registry and injects `webhooks` entries into the generated OpenAPI document. Only definitions with a Zod schema are included.
### Svix sync
`packages/openapi/scripts/svixPush.ts` pushes event types to Svix (create or update). It does **not** delete event types missing from the registry.
### Adding a new webhook event
1. **Create schema** in `shared/api/webhooks/` (Zod schema with `.meta()` descriptions)
2. **Add to registry** in `shared/api/webhooks/webhookRegistry.ts`
3. **Add to `WebhookEventType`** enum in `shared/api/webhooks/webhookEventType.ts`
4. **Re-export** from `shared/api/webhooks/index.ts`
5. **Regenerate** via `bun api` (webhooks are injected into the OpenAPI spec automatically)
6. **Push to Svix** via `bun run --cwd packages/openapi scripts/svixPush.ts`
---
## 7. Testing (sdk-test)
**Location:** `apps/sdk-test/`
Scenario pages organized by provider: `scenarios/core/`, `scenarios/better-auth/`, `scenarios/convex/`.
---
## Adding a New API Route
1. **Add contract** in `packages/openapi/v2.1/contracts/`
2. **Add to router** in `packages/openapi/v2.1/contracts/index.ts`
3. **Regenerate** via `bun api`
4. **Add route definition** in `defaultRoutes.ts`
5. **Add client method** in `AutumnClient.ts`
6. **Add hook** in `react/hooks/` if needed
7. **Add scenario test** in `apps/sdk-test/`
---
## Key File Paths
| Purpose | Path |
|---------|------|
| Zod schemas | `shared/api/` |
| ORPC contracts | `packages/openapi/v2.1/contracts/` |
| Generated TS SDK | `packages/sdk/` |
| Backend core handler | `packages/autumn-js/src/backend/core/handlers/coreHandler.ts` |
| Route definitions | `packages/autumn-js/src/backend/core/routes/defaultRoutes.ts` |
| React client | `packages/autumn-js/src/react/client/AutumnClient.ts` |
| React hooks | `packages/autumn-js/src/react/hooks/` |
| SDK tests | `apps/sdk-test/` |
| Webhook schemas | `shared/api/webhooks/` |
| Webhook registry | `shared/api/webhooks/webhookRegistry.ts` |
| Webhook OpenAPI injection | `packages/openapi/v2.1/webhooks/injectWebhooks.ts` |
| Svix push script | `packages/openapi/scripts/svixPush.ts` |