npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jamx-framework/adapter-cloudflare

v1.0.0

Published

JAMX Framework — Cloudflare Workers adapter

Readme

@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

// 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

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

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

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

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

function createCloudflareHandler(
  handler: JamxHandler
): { fetch(request: Request): Promise<Response> }

Convierte un handler JAMX en un worker exportable.

createCloudflareRouter

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/jsonrequest.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

{
  "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:

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

// 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');
  }
});