npm package: @jamx-framework/adapter-cloudflare
Description: JAMX Framework â€” Cloudflare Workers adapter
Author: Stiven-21
Latest version: 1.0.0

# @jamx-framework/adapter-cloudflare

## Descripción
Adaptador de JAMX Framework para Cloudflare Workers. Proporciona una capa de integración que permite ejecutar handlers y routers de JAMX en el entorno serverless de Cloudflare, convirtiendo las peticiones y respuestas nativas de Cloudflare (Request/Response) a la interfaz unificada de JAMX (JamxRequest/JamxResponseBuilder).

## Cómo funciona
El adaptador implementa dos funciones principales:

1. **createCloudflareHandler**: Convierte un handler JAMX en un worker exportable con método `fetch`
2. **createCloudflareRouter**: Crea un router que selecciona handlers basado en el método HTTP

El flujo de ejecución:
1. Cloudflare Workers recibe una petición HTTP
2. `toJamxRequest()` convierte el `Request` nativo a `JamxRequest` (extrae método, URL, headers, query params, body)
3. Se ejecuta el handler/routers de JAMX con el `JamxRequest` y un `CloudflareResponseBuilder`
4. `CloudflareResponseBuilder` acumula status, headers y body
5. `toResponse()` convierte el builder en un `Response` nativo de Cloudflare
6. El worker devuelve la respuesta

## Componentes principales

### src/adapter.ts
Contiene toda la implementación principal:
- Interfaz `JamxRequest` - Representación unificada de peticiones
- Interfaz `JamxResponseBuilder` - API para construir respuestas
- Clase `CloudflareResponseBuilder` - Implementación concreta
- Funciones `createCloudflareHandler` y `createCloudflareRouter`

### src/index.ts
Punto de entrada público que re-exporta las APIs principales.

## Uso básico

### Handler simple
```typescript
// worker.ts
import { createCloudflareHandler } from '@jamx-framework/adapter-cloudflare';

export default createCloudflareHandler(async (req, res) => {
  res.json({ message: 'Hello from JAMX on Cloudflare Workers!' });
});
```

### Router por método HTTP
```typescript
import { createCloudflareRouter } from '@jamx-framework/adapter-cloudflare';

const router = createCloudflareRouter({
  GET: async (req, res) => {
    res.ok({ method: 'GET', path: req.path });
  },
  POST: async (req, res) => {
    const body = req.body as { name?: string };
    res.created({ name: body?.name });
  },
  PUT: async (req, res) => {
    res.ok({ method: 'PUT' });
  },
  DELETE: async (req, res) => {
    res.noContent();
  },
});

export default router;
```

### Manejo de query params y body
```typescript
export default createCloudflareHandler(async (req, res) => {
  // Query params
  const page = req.query['page'] ?? '1';
  const limit = req.query['limit'] ?? '10';

  // Body (JSON)
  const body = req.body as { name?: string; email?: string } | undefined;

  if (req.method === 'POST' && body?.name && body?.email) {
    // Procesar creación
    res.created({ name: body.name, email: body.email });
  } else {
    // Listar recursos
    res.ok({
      page: Number(page),
      limit: Number(limit),
      items: []
    });
  }
});
```

### Middleware de autenticación
```typescript
const authMiddleware = async (req: JamxRequest, res: JamxResponseBuilder, next: () => void) => {
  const authHeader = req.headers['authorization'];
  if (!authHeader) {
    res.json({ error: 'Unauthorized' }, 401);
    return;
  }

  const token = authHeader.split(' ')[1];
  try {
    const user = await verifyToken(token);
    req.locals.user = user;
    next();
  } catch (err) {
    res.json({ error: 'Invalid token' }, 401);
  }
};

export default createCloudflareHandler(authMiddleware, async (req, res) => {
  res.json({ user: req.locals.user });
});
```

## API Reference

### Tipos

#### JamxRequest
```typescript
interface JamxRequest {
  method: string;           // Método HTTP (GET, POST, etc.)
  url: string;             // URL completa
  path: string;            // Solo el path (sin dominio ni query)
  headers: Record<string, string>;  // Headers normalizados a minúsculas
  query: Record<string, string>;    // Query parameters
  body: unknown;           // Cuerpo de la petición (JSON, texto, etc.)
  params: Record<string, string>;   // Parámetros de ruta (para routers)
  locals: Record<string, unknown>;  // Datos locales para middleware
}
```

#### JamxResponseBuilder
Métodos disponibles:
- `status(code: number): this` - Establece el código de estado
- `header(name: string, value: string): this` - Añade un header
- `json(body: unknown, status?: number): void` - Envía respuesta JSON
- `text(body: string, status?: number): void` - Envía respuesta texto
- `send(body: string, status?: number): void` - Envía body crudo
- `ok(body: unknown): void` - Respuesta 200 OK
- `created(body: unknown): void` - Respuesta 201 Created
- `noContent(): void` - Respuesta 204 No Content
- `notFound(message?: string): void` - Respuesta 404 Not Found
- `raw: { writeHead(status, headers): void; end(body): void; statusCode?: number }` - Acceso a API nativa

### Funciones

#### createCloudflareHandler
```typescript
function createCloudflareHandler(
  handler: JamxHandler
): { fetch(request: Request): Promise<Response> }
```
Convierte un handler JAMX en un worker exportable.

#### createCloudflareRouter
```typescript
function createCloudflareRouter(
  handlers: Partial<Record<string, JamxHandler>>
): { fetch(request: Request): Promise<Response> }
```
Crea un router que selecciona el handler según `req.method`.

## Flujo interno

### Conversión de Request a JamxRequest
1. Se crea un objeto URL desde `request.url`
2. Headers se convierten a `Record<string, string>` con claves en minúsculas
3. Query parameters se extraen de `URL.searchParams`
4. Body se parsea según `Content-Type`:
   - `application/json` → `request.json()`
   - `text/*` → `request.text()`
   - Otros → `undefined`

### Construcción de Response
1. `CloudflareResponseBuilder` acumula internamente:
   - `_status` (default: 200)
   - `_headers` (default: `{ 'content-type': 'application/json' }`)
   - `_body` (string)
2. Los métodos como `json()`, `text()`, etc. establecen status, headers y body
3. `toResponse()` crea un `Response` nativo de Cloudflare con los valores acumulados

### Manejo de raw
La propiedad `raw` proporciona acceso a la API nativa de Cloudflare Workers para casos avanzados donde se necesita control total sobre la respuesta.

## Configuración

### tsconfig.json
```json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "dist",
    "tsBuildInfoFile": "dist/.tsbuildinfo"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "tests"]
}
```

### Scripts disponibles
- `pnpm build` - Compila TypeScript a JavaScript
- `pnpm dev` - Compilación en watch mode
- `pnpm test` - Ejecuta tests unitarios
- `pnpm test:watch` - Tests en watch mode
- `pnpm type-check` - Verifica tipos sin compilar
- `pnpm clean` - Limpia archivos compilados

## Testing
El paquete incluye tests unitarios en `tests/unit/adapter.test.ts` que cubren:
- Conversión de Request/Response
- Parseo de body JSON
- Extracción de query params
- Paso de headers
- Estados de respuesta (204, 404, etc.)
- Enrutamiento por método HTTP
- Respuesta 405 para métodos no soportados

Ejecutar tests:
```bash
pnpm test
```

## Consideraciones de rendimiento
- **Zero-copy headers**: Los headers se copian una sola vez durante la conversión
- **Edge optimized**: Diseñado para el entorno edge de Cloudflare con tiempos de inicio mínimos
- **Sin dependencias externas**: Solo requiere tipos de Cloudflare Workers
- **Streaming**: El body se almacena en memoria como string; para respuestas streaming se necesitaría extensión

## Compatibilidad
- Cloudflare Workers (ES Modules)
- Web API estándar (Request, Response)
- Todos los métodos HTTP
- TypeScript completo con tipos definidos

## Dependencias
- `@types/node` - Tipos de Node.js para desarrollo
- `vitest` - Framework de testing
- `rimraf` - Limpieza de directorios

## Publicación
El paquete está configurado para publicarse en npm como `@jamx-framework/adapter-cloudflare` con acceso público.

## Integración con CLI
- `jamx build` - Compila el adaptador
- `jamx deploy` - Despliega a Cloudflare Workers (requiere Wrangler configurado)

## Ejemplo completo
```typescript
// worker.ts
import { createCloudflareHandler } from '@jamx-framework/adapter-cloudflare';

interface User {
  id: number;
  name: string;
  email: string;
}

// Base de datos simulada
const users: User[] = [];

export default createCloudflareHandler(async (req, res) => {
  switch (req.method) {
    case 'GET':
      res.ok(users);
      break;

    case 'POST':
      const body = req.body as { name: string; email: string };
      if (!body.name || !body.email) {
        res.json({ error: 'Name and email required' }, 400);
        return;
      }
      const newUser: User = {
        id: users.length + 1,
        name: body.name,
        email: body.email,
      };
      users.push(newUser);
      res.created(newUser);
      break;

    default:
      res.notFound('Endpoint not found');
  }
});