Current structure is not tree-shakeable and has a lot of duplicate code.
### 1. The Architecture Problem
#### Current: The Monolith (Not Tree-Shakeable)
You likely generate a class that looks like this:
```typescript
// dist/index.mjs
import { UserSchema, GameSchema, ... } from './schemas'; // Imports ALL 180KB of Schemas
export class LichessClient {
constructor(token) { ... }
// These are all bundled together. You can't separate them.
getUser() { ... } // Uses UserSchema
getGame() { ... } // Uses GameSchema
getTournament() { ... } // Uses TournamentSchema
// ... + 100 other methods
}
```
**Result:** The user imports `LichessClient`, and the bundler sees that `LichessClient` depends on *every single schema*. The whole 300KB+ blob gets included.
#### Target: Functional Composition (Tree-Shakeable)
This is the pattern used by **Firebase v9+**, **Supabase**, and **Octokit**. Instead of a class with methods, you have a lightweight "client" (that just holds state) and standalone functions.
```typescript
// dist/client.mjs
// 1. The Client is just state (URL, token). 0KB logic.
export const createClient = (token) => ({ token, baseUrl: '...' });
// dist/users.mjs
// 2. Functions are standalone.
// This file ONLY imports UserSchema. It does not know about Games or Tournaments.
import { UserSchema } from './schemas/users';
export const getUser = async (client, id) => {
const res = await fetch(`${client.baseUrl}/user/${id}`, ...);
return UserSchema.parse(res);
}
```
**Result:** If the user only imports `getUser`, the bundler **only** includes the code for `getUser` and `UserSchema`. The `GameSchema` and `TournamentSchema` are never touched and are dropped from the bundle.
### 2. Addressing the "Types are 300KB" Issue
You mentioned your client types are 300KB. This suggests **Type Inlining**.
If your generated code looks like this:
```typescript
// generated
export function getUser(id: string): { id: string, username: string, title?: string ... } { ... }
```
TypeScript is repeating the entire User object structure in the `.d.ts` file for every function that uses it.
**The Fix:**
Ensure your generator outputs code that references the *Interface* by name, not value.
```typescript
import type { User } from '../schemas'; // Import the type definition
// generated
export function getUser(id: string): User { ... }
```
This makes your `.d.ts` file tiny because it just points to the definition in `schemas.d.ts` rather than rewriting it.
### 3. A "Hybrid" Approach (for DX)
If you hate the functional syntax (`getUser(client, 'id')`) and prefer the dot notation (`client.users.get('id')`), you can still achieve tree-shaking by using **namespaced exports** or **sub-path exports**, but it is harder.
The functional approach (Option 2 above) is the gold standard for package size.
**Example usage of the new Functional approach:**
```typescript
import { createClient } from '@lichess/api';
import { getUser } from '@lichess/api/users'; // Subpath import
const client = createClient({ token: '...' });
// Only bundles code for getUser and the User Schema
const user = await getUser(client, 'gamerman');
```
### Summary of Next Steps
1. **Modify `yaml-to-client`**: Stop generating a `class`. Start generating standalone functions that accept a `client` object as the first argument.
2. **Check Imports**: Ensure each generated function file only imports the *specific* Zod schema it needs, not a barrel file (`index.ts`) that exports *all* schemas.
3. **Fix Type References**: Ensure the return types reference the named interfaces from `@lichess/api/schemas` to reduce `.d.ts` bloat.
Current structure is not tree-shakeable and has a lot of duplicate code.
Proposed solutions:
1. Tree-shaking:
Generated by Google Gemini